273 Commits

Author SHA1 Message Date
9841c3f29c refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-24 11:59:06 -05:00
b72baf2c65 fix 2025-03-24 11:53:03 -05:00
6244a587db fix 2025-03-24 11:50:52 -05:00
cd41e026ee return available drive letters on windows 2025-03-24 11:35:36 -05:00
10829fc9d9 return available drive letters on windows 2025-03-24 11:23:36 -05:00
4f69ec5f10 updated build system
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-23 18:24:37 -05:00
5dc3a0239f fix release
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-23 10:05:31 -05:00
6b18a72b95 fix missing method
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-03-23 07:19:37 -05:00
0e8e56ad90 fix available port detection
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-03-23 06:24:21 -05:00
e53acf799a prevent overlapping api ports 2025-03-22 16:39:32 -05:00
c2eaa92f4a formatting 2025-03-22 15:53:23 -05:00
13eab49207 release global lock before elevating process 2025-03-22 15:52:30 -05:00
616dca89ca acquire global lock first 2025-03-22 15:50:43 -05:00
9626f383d3 refactor 2025-03-22 15:48:29 -05:00
a262a79eb2 fix 2025-03-22 15:45:46 -05:00
f9ec02bf3f fix 2025-03-22 15:43:45 -05:00
e6793f0d6c fix 2025-03-22 15:38:43 -05:00
0d972b0b75 fix 2025-03-22 15:34:55 -05:00
60f0e3dbc1 refactor 2025-03-22 15:29:08 -05:00
630c3463d8 prevent overlapping api ports 2025-03-22 15:27:50 -05:00
e3d036fcb8 clear mount location if in use
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 13:12:32 -05:00
143a41588f fix
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 10:57:46 -05:00
9fd5b7c03f clear mount list on login failure
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 09:50:24 -05:00
6137b69bd3 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 09:24:40 -05:00
c52c919607 remove advanced from add
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 07:38:16 -05:00
58841986f3 added logoff button
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-22 07:37:09 -05:00
874f5319fd layout changes
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 07:25:15 -05:00
c05b4f6652 fixes
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 02:52:48 -05:00
aad26b8529 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 02:23:13 -05:00
e4f59dadfb refactoring
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 02:20:03 -05:00
f8b1b8ad37 return unauthorized when decryption fails 2025-03-22 01:47:40 -05:00
03c8f3461e continue authentication 2025-03-22 01:42:20 -05:00
5b09333f0d partial logon support
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-22 01:25:30 -05:00
40e57f3262 remove debug logging
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 22:31:36 -05:00
959d904961 fork() on linux
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 22:30:11 -05:00
d301e8d871 prefer execvp and _spawnv
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 22:04:45 -05:00
1cc3e6baf0 added show/hide password buttons
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 17:56:31 -05:00
f5b912b16f updated build system
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 17:39:39 -05:00
29f7a78dcc fix password
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 17:29:58 -05:00
56d5a57831 fix add mount
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-21 10:48:10 -05:00
972927b2ac refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-20 22:06:22 -05:00
42c25b73e7 removed debugPrint 2025-03-20 22:00:00 -05:00
ea876bd55c refactor
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-20 21:56:22 -05:00
6d2023ba1b fix
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-20 21:52:18 -05:00
164f8ffc7c fix mount settings encrypt
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-20 20:53:36 -05:00
a28151068a refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-20 18:39:42 -05:00
674a5a6fe1 prompt for password once 2025-03-20 18:37:03 -05:00
d9b8a60055 prompt for password once 2025-03-20 18:36:10 -05:00
6570438872 fix 2025-03-20 18:19:59 -05:00
12ef6910ed fix 2025-03-20 18:15:31 -05:00
225a5cedc5 don't decrypt empty values
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-20 17:56:11 -05:00
b42dc4e942 fix 2025-03-20 17:51:21 -05:00
b72744716d prompt password
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-20 17:33:32 -05:00
1b6237b9fc fix
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-20 17:04:13 -05:00
bdb8f0aac9 fix
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-03-20 13:49:11 -05:00
29fb70149c encrypt/decrypt secret data 2025-03-20 12:59:23 -05:00
4b45e71193 encrypt/decrypt secret data 2025-03-20 12:54:43 -05:00
0101e92d97 fix 2025-03-20 11:39:35 -05:00
ba5bde24e1 fix 2025-03-20 09:33:10 -05:00
40d71223ae remove passwords from api calls 2025-03-20 09:12:50 -05:00
e73fb02101 remove passwords from api calls 2025-03-20 08:55:10 -05:00
7f300b33c8 remove passwords from api calls 2025-03-20 08:51:50 -05:00
c4c509790d remove passwords from api calls 2025-03-20 08:32:20 -05:00
1ac64c9d82 remove passwords from api calls 2025-03-20 08:09:45 -05:00
c037fd5657 remove passwords from api calls 2025-03-20 08:08:43 -05:00
9b9929e69d remove passwords from api calls 2025-03-20 08:05:00 -05:00
5104af84dc added sodium support
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-19 09:01:05 -05:00
ab42289792 formatting
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-19 08:38:05 -05:00
0e4ebd7742 added sodium support 2025-03-19 08:34:34 -05:00
ea912b38a3 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-19 08:20:15 -05:00
f015647b71 refactor
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-19 08:17:04 -05:00
bced895ea1 fix mounting across multiple instances
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-19 08:13:14 -05:00
901000a085 refactor 2025-03-19 07:44:07 -05:00
d5242c2e6c refactor 2025-03-19 07:43:47 -05:00
7c4cba6797 fix json parsing 2025-03-19 07:41:59 -05:00
10a0e926ad fix 2025-03-18 14:11:48 -05:00
9fcc50fcc3 wait for mount/unmount to complete
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-18 07:47:40 -05:00
d57fa022d8 wait for mount/unmount to complete 2025-03-18 07:30:54 -05:00
ee1638e1dd return null
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-17 13:51:09 -05:00
d125cb47d6 fix route 2025-03-17 13:35:08 -05:00
16bb4fe472 mount status fix 2025-03-17 13:18:36 -05:00
984f4e0bc9 windows fixes 2025-03-17 13:08:01 -05:00
439be1dea8 windows fixes 2025-03-17 12:54:38 -05:00
c9281c9bcd windows fixes 2025-03-17 12:50:16 -05:00
b15393bacf updated README.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 16:44:22 -05:00
f3c0f4978f updated README.md
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-16 16:42:42 -05:00
78ba8553a9 updated README.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 16:41:46 -05:00
2cc646390a revert image 2025-03-16 16:35:38 -05:00
87f5b0f953 updated README.md 2025-03-16 16:34:47 -05:00
c60bb651df revert 2025-03-16 16:33:23 -05:00
6fb8053e43 updated README.md 2025-03-16 16:32:50 -05:00
4c18088bd1 updated README.md 2025-03-16 16:31:00 -05:00
a5eb168032 updated README.md
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-16 16:29:21 -05:00
187e2454ca updated README.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 16:23:34 -05:00
931c861850 remove mount locations
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 15:56:22 -05:00
2a1abd0bb0 save ui settings
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 15:52:46 -05:00
c75ce9ad21 save ui settings 2025-03-16 15:41:50 -05:00
9afc8e3cb6 save ui settings 2025-03-16 15:31:34 -05:00
56beeacbb3 updated CHANGELOG.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 09:50:35 -05:00
4253700fe7 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 08:16:00 -05:00
2d638d6335 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 08:13:41 -05:00
7823627340 added portal settings screen 2025-03-16 08:11:04 -05:00
e7accd1cc1 added portal settings screen 2025-03-16 08:09:42 -05:00
9b5d642106 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 07:09:10 -05:00
e167079e5d layout changes
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-16 07:08:02 -05:00
1081ff0b19 added setting description
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-16 06:53:06 -05:00
c720181208 refactor
Some checks are pending
BlockStorage/repertory/pipeline/head Build started...
2025-03-16 06:27:37 -05:00
d7a0fd9d8c fix
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 23:45:19 -05:00
2a7a409506 fix 2025-03-15 23:38:00 -05:00
97310e2ba9 handle reset
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-15 23:28:52 -05:00
54fe6d7bab fix 2025-03-15 23:24:51 -05:00
f70044b9a8 fix
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 23:10:30 -05:00
ea30396e49 refactor 2025-03-15 23:09:04 -05:00
985e503c21 updated CHANGELOG.md 2025-03-15 23:07:34 -05:00
b959af26c4 refactor 2025-03-15 23:04:18 -05:00
0add5ec944 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 22:56:23 -05:00
cd9ad1e322 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 22:50:33 -05:00
ca8de7f87d refactor 2025-03-15 22:45:26 -05:00
2ff72eebce refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 22:31:21 -05:00
97fce78370 refactor 2025-03-15 22:22:28 -05:00
21af9be9a8 refactor
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-15 22:20:38 -05:00
23de275e11 remote mount provider support
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 22:18:42 -05:00
28cfcc0344 remote mount provider support 2025-03-15 22:01:29 -05:00
30a91e1cb2 remote mount provider support 2025-03-15 21:56:10 -05:00
b2d4baa903 remote mount provider support 2025-03-15 21:52:01 -05:00
d3070ffee1 ui changes
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 21:37:47 -05:00
abd7e24b5e refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 19:20:58 -05:00
462ebe6fcf detect port availability 2025-03-15 19:18:35 -05:00
b93e2978b0 cleanup
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 19:01:29 -05:00
ece002f25b fixes 2025-03-15 19:00:17 -05:00
b9f5f774e2 display error if mount location is not found 2025-03-15 18:43:43 -05:00
52d210974b display error if mount location is not found 2025-03-15 18:34:49 -05:00
7605be809c per-instance locking
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 18:25:03 -05:00
d0f2f78698 handle mount location not found 2025-03-15 18:14:21 -05:00
69c574b906 ignore existing
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 13:31:08 -05:00
67a669ee17 refactor 2025-03-15 13:24:36 -05:00
c82f07056d refactor
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-15 13:20:53 -05:00
196d721005 reset on error
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 13:17:54 -05:00
532dd3c417 ui changes
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 11:35:15 -05:00
c4b4bc9b49 ui changes 2025-03-15 11:33:00 -05:00
343ac025d5 cleanup 2025-03-15 11:25:51 -05:00
d08fd52e51 refactor
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-15 11:22:03 -05:00
05f323665a refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-15 10:59:01 -05:00
349bede3b3 fixes 2025-03-15 10:57:56 -05:00
1a7dc51c4b fixes 2025-03-15 09:13:18 -05:00
d209b52848 prevent duplicates 2025-03-15 08:42:11 -05:00
492ccfbdfb fix
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 21:20:35 -05:00
678dfd6c8b fix 2025-03-14 21:17:37 -05:00
bc146a48a7 fix 2025-03-14 21:12:10 -05:00
04523df564 refactor 2025-03-14 21:03:30 -05:00
b1a31b230a refactor 2025-03-14 20:59:45 -05:00
fe08e274ec don't allow mount until state is determined 2025-03-14 20:57:44 -05:00
33e0066b4c synchronous command 2025-03-14 20:49:14 -05:00
ce814e96a6 mount fixes
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-14 20:46:30 -05:00
7dd56cf715 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 20:38:21 -05:00
79262ef862 update default credentials
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 20:13:32 -05:00
e270867bf2 fix 2025-03-14 20:07:05 -05:00
6b629b81b4 removed logging
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-14 19:59:13 -05:00
812f68422b fix 2025-03-14 19:57:16 -05:00
76c105286f Create management portal in Flutter #39 2025-03-14 19:55:32 -05:00
cd9ac4a02f add mount via ui 2025-03-14 19:33:05 -05:00
6d09b2549e layout changes
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 19:19:36 -05:00
69f27b2e69 Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 19:08:44 -05:00
7cbf1a3937 Create management portal in Flutter #39 2025-03-14 19:06:25 -05:00
dc817797f3 Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 18:57:33 -05:00
71789f6cdb Create management portal in Flutter #39 2025-03-14 18:57:04 -05:00
6e8f843252 Create management portal in Flutter #39 2025-03-14 18:54:31 -05:00
f72f86d8ae Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 18:48:34 -05:00
45ea5bab8f Create management portal in Flutter #39 2025-03-14 18:41:53 -05:00
9d083a1d93 Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 16:18:35 -05:00
85beb3dfea refactor 2025-03-14 16:17:25 -05:00
3eb4ad85d3 revert 2025-03-14 15:53:45 -05:00
e45119bbbf Create management portal in Flutter #39 2025-03-14 15:52:09 -05:00
4326169186 Create management portal in Flutter #39 2025-03-14 15:48:37 -05:00
e32233a936 Create management portal in Flutter #39 2025-03-14 15:45:07 -05:00
6e8273f9bc Create management portal in Flutter #39 2025-03-14 15:43:33 -05:00
863e316b8a Create management portal in Flutter #39 2025-03-14 15:40:14 -05:00
07311cd710 Create management portal in Flutter #39 2025-03-14 15:28:56 -05:00
637f123c24 Create management portal in Flutter #39 2025-03-14 15:24:01 -05:00
6602e7eff6 Create management portal in Flutter #39 2025-03-14 15:22:07 -05:00
0fd2dc3ddb Create management portal in Flutter #39
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-14 14:54:40 -05:00
d920a55fc0 Create management portal in Flutter #39 2025-03-14 14:47:23 -05:00
edd27e3e34 Create management portal in Flutter #39 2025-03-14 14:33:55 -05:00
6a8a163d0a update
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-14 11:02:16 -05:00
99fa7ffc15 update
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
2025-03-14 10:54:34 -05:00
957c1f5256 update 2025-03-14 10:53:35 -05:00
0214bcff6d updated build system
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-03-14 10:33:26 -05:00
9173ff8d4d updates
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-03-14 10:07:28 -05:00
302cd43a03 update 2025-03-14 10:07:20 -05:00
fe51811b39 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-07 12:32:23 -06:00
35e52d50a7 Create management portal in Flutter #39 2025-03-07 12:31:33 -06:00
eb7bfdddd1 Create management portal in Flutter #39 2025-03-07 12:31:00 -06:00
be6ee3ba07 refactor 2025-03-07 11:39:01 -06:00
4dbe4a30b4 refactor 2025-03-07 11:38:42 -06:00
1d37822451 Create management portal in Flutter #39 2025-03-07 11:36:43 -06:00
5ed74aa5af refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-06 15:13:35 -06:00
94073fe1f1 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-06 14:05:11 -06:00
992a02a6ee refactor 2025-03-06 14:04:05 -06:00
6f45848db4 refactor 2025-03-06 13:46:39 -06:00
918d7172ec refactor 2025-03-06 12:51:29 -06:00
8da6008e29 Create management portal in Flutter #39 2025-03-06 12:50:31 -06:00
d2065af398 Create management portal in Flutter #39 2025-03-06 11:58:18 -06:00
b4fd093e7c Create management portal in Flutter #39 2025-03-06 11:51:39 -06:00
5e47cdb861 Create management portal in Flutter #39 2025-03-06 11:40:08 -06:00
eb8f66ebe9 Create management portal in Flutter #39 2025-03-06 11:32:25 -06:00
72a2567c83 Create management portal in Flutter #39 2025-03-06 10:03:35 -06:00
c1e5bd6b0b Create management portal in Flutter #39 2025-03-06 08:53:36 -06:00
02157d21ea Create management portal in Flutter #39 2025-03-06 08:44:17 -06:00
0318489b6c Create management portal in Flutter #39 2025-03-06 08:19:30 -06:00
847bf68f85 refactor
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-05 20:38:15 -06:00
6e42899dd3 Create management portal in Flutter #39 2025-03-05 20:26:44 -06:00
35a794106b Create management portal in Flutter #39
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-03-05 20:25:51 -06:00
c54838ad6f fix display
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-05 20:21:51 -06:00
58fde34cfe Create management portal in Flutter #39 2025-03-05 20:19:42 -06:00
3a72563a5c Create management portal in Flutter #39 2025-03-05 19:59:49 -06:00
9e2ad81cff updates
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-05 14:43:42 -06:00
18a66953f2 fix initial file creation
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-05 14:34:31 -06:00
b2c0f44a7d fix initial file creation 2025-03-05 14:24:17 -06:00
791472455a create root directory 2025-03-05 14:11:30 -06:00
8638de87f9 save on error 2025-03-05 14:10:09 -06:00
9409622fef Create management portal in Flutter #39 2025-03-05 14:03:11 -06:00
f2db24b239 Create management portal in Flutter #39 2025-03-05 10:46:00 -06:00
199aea55be Create management portal in Flutter #39 2025-03-05 10:41:25 -06:00
8a18e7a4c9 Create management portal in Flutter #39 2025-03-05 10:31:18 -06:00
dce856b2be Require --name,-na option for encryption provider 2025-03-05 09:45:50 -06:00
51ee46e279 Create management portal in Flutter #39 2025-03-05 09:34:57 -06:00
a0bf5ec3d2 Require --name,-na option for encryption provider 2025-03-05 08:20:24 -06:00
b74160bfb3 Create management portal in Flutter #39 2025-03-05 08:17:27 -06:00
e35f43af97 Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-04 19:45:43 -06:00
e9d65bb566 Create management portal in Flutter #39 2025-03-04 19:43:17 -06:00
15f6b116cc Create management portal in Flutter #39 2025-03-04 19:42:13 -06:00
71f3567375 Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-04 14:48:37 -06:00
865ed5f0c1 Create management portal in Flutter #39 2025-03-04 14:02:28 -06:00
661f57d599 refactor 2025-03-04 13:34:02 -06:00
badd098fe0 Create management portal in Flutter #39 2025-03-04 13:32:15 -06:00
820617b3ff refactor 2025-03-04 13:06:23 -06:00
717b461eaf Create management portal in Flutter #39 2025-03-04 12:57:59 -06:00
4b3890809d updated CHANGELOG.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-03 19:58:15 -06:00
d9cd2aa88a Create management portal in Flutter (#40)
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
Reviewed-on: #40
2025-03-03 19:56:56 -06:00
c59c846856 updated build system
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-01 11:22:49 -06:00
1a400cac8d updated build system
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
2025-03-01 11:18:01 -06:00
57b956b60c updated build system
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
2025-02-28 18:29:32 -06:00
b95d69ccb8 updated build system
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-02-28 17:44:08 -06:00
d90a0eab3d updated build system
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
2025-02-28 17:35:05 -06:00
c6448cc64c updated build system
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
2025-02-28 15:01:54 -06:00
514e9535e2 updated build system
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-02-28 13:37:36 -06:00
14cee39a20 fix
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
2025-02-28 12:10:32 -06:00
2c510346f2 updated build system
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-02-28 11:06:47 -06:00
1560804df8 fixed config name
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-24 09:49:26 -06:00
cc3c0febc3 updated README.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 18:23:04 -06:00
0458f12e17 Update README.md 2025-02-23 18:21:57 -06:00
ae43cedb45 Update README.md 2025-02-23 18:21:15 -06:00
191eb1620b Update README.md
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-02-23 18:20:52 -06:00
9c648583fb Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 18:19:52 -06:00
14d78d0b65 cleanup
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 16:34:12 -06:00
54efde0497 Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 16:31:33 -06:00
5f593ab86d Complete initial v2.0 documentation #33 2025-02-23 16:30:23 -06:00
feb09746f5 Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 11:19:16 -06:00
faad98c11e Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 11:18:18 -06:00
12f04c6064 revert
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 00:32:04 -06:00
ca307c3bf2 update
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 00:29:44 -06:00
ac6f4bcade Complete initial v2.0 documentation #33
Some checks are pending
BlockStorage/repertory/pipeline/head Build queued...
2025-02-23 00:27:11 -06:00
9d48cd97e3 Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 00:20:51 -06:00
6c6fb0554f updated CHANGELOG.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-23 00:01:24 -06:00
a134f01436 updated CHANGELOG.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-22 23:56:21 -06:00
44c33652fa cleanup 2025-02-22 23:55:46 -06:00
131c36415d Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-22 23:06:38 -06:00
9456c8b1d2 Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-22 23:00:26 -06:00
b8c62612d8 refactor 2025-02-22 22:01:37 -06:00
521874a56f Complete initial v2.0 documentation #33
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-22 19:59:22 -06:00
f41ad47262 updated CHANGES.md
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-02-22 11:36:20 -06:00
8bb2eeb88c updated CHANGELOG.md
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2025-02-22 10:07:52 -06:00
b1aca46034 updated version 2025-02-22 09:58:57 -06:00
130 changed files with 6403 additions and 549 deletions

View File

@ -3,6 +3,7 @@ _mkgmtime
_sh_denyno _sh_denyno
_sh_denyrd _sh_denyrd
_sh_denyrw _sh_denyrw
_spawnv
aarch64 aarch64
advapi32 advapi32
armv8 armv8
@ -121,6 +122,7 @@ icui18n
icuuc icuuc
iostreams iostreams
iphlpapi iphlpapi
ipstream
jthread jthread
libbitcoin libbitcoin
libbitcoinsystem libbitcoinsystem
@ -154,8 +156,10 @@ mtune
musl-libc musl-libc
nana nana
ncrypt ncrypt
nlohmann
nlohmann_json nlohmann_json
nmakeprg nmakeprg
nohup
nominmax nominmax
ntstatus ntstatus
nullptr nullptr
@ -163,6 +167,7 @@ nuspell_version
oleaut32 oleaut32
openal_version openal_version
openssldir openssldir
pistream
pkgconfig pkgconfig
plarge_integer plarge_integer
plex plex

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.tgz filter=lfs diff=lfs merge=lfs -text
*.tar.gz filter=lfs diff=lfs merge=lfs -text
*.tar.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text

View File

@ -1,11 +1,29 @@
# Changelog # Changelog
## v2.0.5-rc
### Issues
* ~~\#12 [Unit Test] Complete all providers unit tests~~
* ~~\#21 [Unit Test] Complete WinFSP unit tests~~
* ~~\#22 [Unit Test] Complete FUSE unit tests~~
* \#39 Create management portal in Flutter
### Changes from v2.0.4-rc
* Continue documentation updates
* Prevent overlapping `repertory` `ApiPort`'s
* Removed passwords and secret key values from API calls
* Renamed setting `ApiAuth` to `ApiPassword`
* Require `--name,-na` option for encryption provider
## v2.0.4-rc ## v2.0.4-rc
### BREAKING CHANGES ### BREAKING CHANGES
* `renterd` v2.0.0+ is now required. Prior versions will fail to mount. * `renterd` v2.0.0+ is now required. Prior versions will fail to mount.
<!-- markdownlint-disable-next-line -->
### Issues ### Issues
* \#35 [bug] Low frequency check is set to '0' instead of 1 hour by default * \#35 [bug] Low frequency check is set to '0' instead of 1 hour by default

374
README.md
View File

@ -3,11 +3,42 @@
Repertory allows you to mount S3 and Sia via FUSE on Linux or via WinFSP Repertory allows you to mount S3 and Sia via FUSE on Linux or via WinFSP
on Windows. on Windows.
## Contents
1. [Details and Features](#details-and-features)
2. [Minimum Requirements](#minimum-requirements)
1. [Supported Operating Systems](#supported-operating-systems)
3. [GUI](#gui)
4. [Usage](#usage)
1. [Important Options](#important-options)
2. [Sia](#sia)
* [Sia Initial Configuration](#sia-initial-configuration)
* [Sia Mounting](#sia-mounting)
* [Sia Configuration File](#sia-configuration-file)
3. [S3](#s3)
* [S3 Initial Configuration](#s3-initial-configuration)
* [S3 Mounting](#s3-mounting)
* [S3 Configuration File](#s3-configuration-file)
5. [Data Directories](#data-directories)
1. [Linux Directories](#linux-directories)
2. [Windows Directories](#windows-directories)
6. [Remote Mounting](#remote-mounting)
1. [Server Setup](#server-setup)
* [Remote Mount Configuration File Section](#remote-mount-configuration-file-section)
2. [Client Setup](#client-setup)
* [Client Remote Mounting](#client-remote-mounting)
* [Remote Mount Configuration File](#remote-mount-configuration-file)
7. [Compiling](#compiling)
1. [Linux Compilation](#linux-compilation)
2. [Windows Setup](#windows-compilation)
8. [Credits](#credits)
9. [Developer Public Key](#developer-public-key)
10. [Consult the Wiki for additional information](https://git.fifthgrid.com/BlockStorage/repertory/wiki)
## Details and Features ## Details and Features
* Optimized for [Plex Media Server](https://www.plex.tv/) * Optimized for [Plex Media Server](https://www.plex.tv/)
* Single application to mount S3 and/or Sia * Remote mounting of `repertory` instances on Linux and Windows
* Remote mounting of Repertory instances on Linux and Windows
* Securely share your mounts over TCP/IP via `XChaCha20-Poly1305` with other systems on your network or over the internet. * Securely share your mounts over TCP/IP via `XChaCha20-Poly1305` with other systems on your network or over the internet.
* Cross-platform support (Linux 64-bit, Linux arm64/aarch64, Windows 64-bit) * Cross-platform support (Linux 64-bit, Linux arm64/aarch64, Windows 64-bit)
* Optionally encrypt file names and file data via `XChaCha20-Poly1305` in S3 mounts * Optionally encrypt file names and file data via `XChaCha20-Poly1305` in S3 mounts
@ -15,52 +46,159 @@ on Windows.
## Minimum Requirements ## Minimum Requirements
* [Sia renterd](https://github.com/SiaFoundation/renterd/releases) v2.0.0+ for Sia support * [Sia renterd](https://github.com/SiaFoundation/renterd/releases) v2.0.0+ for Sia support
* Only 64-bit operating systems are supported * Linux requires `fusermount3`; otherwise, `repertory` must be manually compiled with `libfuse2` support
* By default, Linux requires `fusermount3`; otherwise, `repertory` must be manually compiled with `libfuse2` support * Windows requires the following dependencies to be installed:
* Windows requires the following dependencies to be installed:
* [WinFSP 2023](https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi) * [WinFSP 2023](https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi)
## Supported Operating Systems ### Supported Operating Systems
Only 64-bit operating systems are supported
* Linux `arm64/aarch64` * Linux `arm64/aarch64`
* Linux `amd64` * Linux `amd64`
* Windows 64-bit 10, 11 * Windows 64-bit 10, 11
## GUI
As of `v2.0.5-rc`, mounts can be managed using the `Repertory Management Portal`.
To launch the portal, execute the following command:
* `repertory -ui`
* The default username is `repertory`
* The default password is `repertory`
After first launch, `ui.json` will be created in the appropriate data directory.
See [Data Directories](#data-directories).
You should modify this file directly or use the portal to change the default
username and password.
### Screenshot
<a href="https://ibb.co/fVyJqnbF"><img src="https://i.ibb.co/fVyJqnbF/repertory-portal.png" alt="repertory-portal" border="0"></a>
## Usage ## Usage
### Important Options
* `--help`
* Display all mount utility options
* `-f`
* Keep process in foreground on Linux.
* `--name, -na [name]`
* Identifies a unique configuration name to support multiple mounts.
* The `--name` option can be set to any valid value allowed as a file name for your filesystem.
* For Sia, the bucket name will be set to the same value if it is empty in the configuration file.
* If the `--name` option is not specified, `default` will be used.
* For S3, the `--name` option is required and does not affect the bucket name.
* `-dc`
* Display mount configuration
* For Sia, `--name` is optional
* For S3, the `-s3` option is required along with `--name`
### Sia ### Sia
* Initial Configuration #### Sia Initial Configuration
* Sia steps:
* Required steps:
* Set the appropriate bucket name and `renterd` API password in `repertory` configuration: * Set the appropriate bucket name and `renterd` API password in `repertory` configuration:
* To use `default` as the bucket name and configuration name: * To use `default` as the bucket name and configuration name, you only need to set the `renterd` API password:
* `repertory -set HostConfig.ApiPassword '<my password>'` * `repertory -set HostConfig.ApiPassword '<my password>'`
* To use a different bucket name with `default` as the configuration name: * To specify a different bucket name while using `default` as the configuration name:
* `repertory -set HostConfig.ApiPassword '<my password>'` * `repertory -set HostConfig.ApiPassword '<my password>'`
* `repertory -set SiaConfig.Bucket '<my bucket>'` * `repertory -set SiaConfig.Bucket '<my bucket>'`
* For all other configurations: * For all other configurations:
* `repertory --name '<my config name>' -set HostConfig.ApiPassword '<my password>'` * `repertory --name '<my config name>' -set HostConfig.ApiPassword '<my password>'`
* `repertory --name '<my config name>' -set SiaConfig.Bucket '<my bucket name>'` * `repertory --name '<my config name>' -set SiaConfig.Bucket '<my bucket name>'`
* To verify/view all configuration options:
* Optional steps:
* Set a user name used during `renterd` basic authentication:
* `repertory -set HostConfig.ApiUser '<my user>'`
* `repertory --name '<my config name>' -set HostConfig.ApiUser '<my user>'`
* Set a custom agent string (default `Sia-Agent`):
* `repertory -set HostConfig.AgentString '<my agent>'`
* `repertory --name '<my config name>' -set HostConfig.AgentString '<my agent>'`
* Set the host name or IP of the `renterd` instance (default `localhost`):
* `repertory -set HostConfig.HostNameOrIp '<my host name>'`
* `repertory --name '<my config name>' -set HostConfig.HostNameOrIp '<my host name>'`
* Set the `renterd` API port (default `9980`):
* `repertory -set HostConfig.ApiPort 9981`
* `repertory --name '<my config name>' -set HostConfig.ApiPort 9981`
* To verify/view all configuration options:
* `repertory -dc` * `repertory -dc`
* `repertory --name '<my config name>' -dc` * `repertory --name '<my config name>' -dc`
* Example: * Example:
* `repertory --name default -dc` * `repertory --name default -dc`
* Mounting on Linux:
#### Sia Mounting
* Linux:
* `repertory /mnt/location` * `repertory /mnt/location`
* `repertory --name '<my config name>' /mnt/location` * `repertory --name '<my config name>' /mnt/location`
* Example: * Example:
* `repertory --name default /mnt/location` * `repertory --name default /mnt/location`
* Mounting on Windows:
* Windows:
* `repertory t:` * `repertory t:`
* `repertory --name '<my config name>' t:` * `repertory --name '<my config name>' t:`
* Example: * Example:
* `repertory --name default t:` * `repertory --name default t:`
#### Sia Configuration File
```json
{
"ApiPassword": "<random generated rpc password>",
"ApiPort": 10000,
"ApiUser": "repertory",
"DatabaseType": "rocksdb",
"DownloadTimeoutSeconds": 30,
"EnableDownloadTimeout": true,
"EnableDriveEvents": false,
"EventLevel": "info",
"EvictionDelayMinutes": 1,
"EvictionUseAccessedTime": false,
"HighFreqIntervalSeconds": 30,
"HostConfig": {
"AgentString": "Sia-Agent",
"ApiPassword": "<renterd api password>",
"ApiPort": 9980,
"ApiUser": "",
"HostNameOrIp": "localhost",
"Path": "",
"Protocol": "http",
"TimeoutMs": 60000
},
"LowFreqIntervalSeconds": 3600,
"MaxCacheSizeBytes": 21474836480,
"MaxUploadCount": 5,
"MedFreqIntervalSeconds": 120,
"OnlineCheckRetrySeconds": 60,
"PreferredDownloadType": "default",
"RemoteMount": {
"ApiPort": 20000,
"ClientPoolSize": 20,
"Enable": false,
"EncryptionToken": ""
},
"RetryReadCount": 6,
"RingBufferFileSize": 512,
"SiaConfig": {
"Bucket": "default"
},
"TaskWaitMs": 100,
"Version": 1
}
```
### S3 ### S3
* Initial Configuration #### S3 Initial Configuration
* S3 steps:
* Required steps:
* Set the appropriate base URL: * Set the appropriate base URL:
* `repertory -s3 --name '<my config name>' -set S3Config.URL '<my url>'` * `repertory -s3 --name '<my config name>' -set S3Config.URL '<my url>'`
* Example: * Example:
@ -73,75 +211,219 @@ on Windows.
* `repertory -s3 --name '<my config name>' -set S3Config.SecretKey '<my secret key>'` * `repertory -s3 --name '<my config name>' -set S3Config.SecretKey '<my secret key>'`
* For Sia and most local S3 gateway instances, enable path style URL's: * For Sia and most local S3 gateway instances, enable path style URL's:
* `repertory -s3 --name '<my config name>' -set S3Config.UsePathStyle true` * `repertory -s3 --name '<my config name>' -set S3Config.UsePathStyle true`
* Optional steps:
* Optional steps:
* Set an appropriate region. Default is set to `any`: * Set an appropriate region. Default is set to `any`:
* `repertory -s3 --name '<my config name>' -set S3Config.Region '<my region>'` * `repertory -s3 --name '<my config name>' -set S3Config.Region '<my region>'`
* Enable encrypted file names and file data. Set a strong, random encryption token and be sure to store it in a secure backup location: * Enable encrypted file names and file data. Set a strong, random encryption token and be sure to store it in a secure backup location:
* `repertory -s3 --name '<my config name>' -set S3Config.EncryptionToken '<my strong password>'` * `repertory -s3 --name '<my config name>' -set S3Config.EncryptionToken '<my strong password>'`
* To verify/view all configuration options:
* To verify/view all configuration options:
* `repertory -s3 --name '<my config name>' -dc` * `repertory -s3 --name '<my config name>' -dc`
* Example: * Example:
* `repertory -s3 --name minio -dc` * `repertory -s3 --name minio -dc`
* Mounting on Linux:
#### S3 Mounting
* Linux:
* `repertory -s3 --name '<my config name>' /mnt/location` * `repertory -s3 --name '<my config name>' /mnt/location`
* Example: * Example:
* `repertory -s3 --name minio /mnt/location` * `repertory -s3 --name minio /mnt/location`
* Mounting on Windows:
* Windows:
* `repertory -s3 --name '<my config name>' t:` * `repertory -s3 --name '<my config name>' t:`
* Example: * Example:
* `repertory -s3 --name minio t:` * `repertory -s3 --name minio t:`
### Notable Options #### S3 Configuration File
* `--help` ```json
* Display all mount utility options {
* `--name, -na [name]` "ApiPassword": "<random generated rpc password>",
* The `--name` option can be set to any valid value allowed as a file name for your filesystem. "ApiPort": 10100,
* For Sia, the bucket name will be set to the same value if it is empty in the configuration file. "ApiUser": "repertory",
* If the `--name` option is not specified, `default` will be used. "DatabaseType": "rocksdb",
* For S3, the `--name` option is required and does not affect the bucket name. "DownloadTimeoutSeconds": 30,
* `-dc` "EnableDownloadTimeout": true,
* Display mount configuration "EnableDriveEvents": false,
* For Sia, `--name` is optional "EventLevel": "info",
* For S3, the `-s3` option is required along with `--name` "EvictionDelayMinutes": 1,
"EvictionUseAccessedTime": false,
"HighFreqIntervalSeconds": 30,
"LowFreqIntervalSeconds": 3600,
"MaxCacheSizeBytes": 21474836480,
"MaxUploadCount": 5,
"MedFreqIntervalSeconds": 120,
"OnlineCheckRetrySeconds": 60,
"PreferredDownloadType": "default",
"RemoteMount": {
"ApiPort": 20100,
"ClientPoolSize": 20,
"Enable": false,
"EncryptionToken": ""
},
"RetryReadCount": 6,
"RingBufferFileSize": 512,
"S3Config": {
"AccessKey": "<my access key>",
"Bucket": "<my bucket name>",
"EncryptionToken": "",
"Region": "any",
"SecretKey": "<my secret key>",
"TimeoutMs": 60000,
"URL": "http://localhost:9000",
"UsePathStyle": true,
"UseRegionInURL": false
},
"TaskWaitMs": 100,
"Version": 1
}
```
### Data Directories ### Data Directories
* Linux #### Linux Directories
* `~/.local/repertory2`
* Windows * `~/.local/repertory2/s3`
* `%LOCALAPPDATA%\repertory2` * `~/.local/repertory2/sia`
* Example:
* `C:\Users\Tom\AppData\Local\repertory2` #### Windows Directories
* IMPORTANT:
* `%LOCALAPPDATA%\repertory2\s3`
* `%LOCALAPPDATA%\repertory2\sia`
* Examples:
* `C:\Users\Tom\AppData\Local\repertory2\s3`
* `C:\Users\Tom\AppData\Local\repertory2\sia`
* IMPORTANT:
* It is highly recommended to exclude this folder from any anti-virus/anti-malware applications as severe performance issues may arise. * It is highly recommended to exclude this folder from any anti-virus/anti-malware applications as severe performance issues may arise.
* Excluding the mounted drive letter is also highly recommended. * Excluding the mounted drive letter is also highly recommended.
## Remote Mounting ## Remote Mounting
`Repertory` allows local mounts to be shared with other computers on your network. `repertory` allows local mounts to be shared with other computers on your network
This option is referred to as remote mounting. Instructions TBD. or over the internet. This option is referred to as remote mounting.
### Server Setup
The following steps must be performed on the mount you wish to share with
other systems. Changes to configuration will not take affect while a mount is
active, so it is recommended to unmount beforehand.
* Required steps:
* Enable remote mount:
* Sia
* `repertory -set RemoteMount.Enable true`
* `repertory --name '<my config name>' -set RemoteMount.Enable true`
* S3:
* `repertory -s3 --name '<my config name>' -set RemoteMount.Enable true`
* Set a secure encryption token:
* Sia:
* `repertory -set RemoteMount.EncryptionToken '<my secure password>'`
* `repertory --name '<my config name>' -set RemoteMount.EncryptionToken '<my secure password>'`
* S3:
* `repertory -s3 --name '<my config name>' -set RemoteMount.EncryptionToken '<my secure password>'`
* Optional steps:
* Change the port clients will use to connect to your mount:
* Sia:
* `repertory -set RemoteMount.ApiPort 20000`
* `repertory --name '<my config name>' -set RemoteMount.ApiPort 20000`
* S3:
* `repertory -s3 --name '<my config name>' -set RemoteMount.ApiPort 20000`
* IMPORTANT:
* Be sure to configure your firewall to allow incoming TCP connections on the port configured in `RemoteMount.ApiPort`.
#### Remote Mount Configuration File Section
```json
{
...
"RemoteMount": {
"ApiPort": 20000,
"ClientPoolSize": 20,
"Enable": true,
"EncryptionToken": "<my secure password>"
},
...
}
```
### Client Setup
Client configuration is provider agnostic, so there's no need to specify `-s3`
for S3 providers.
* Required steps:
* Set the encryption token to the same value configured during server setup:
* `repertory -rm <host name or IP>:<port> -set RemoteConfig.EncryptionToken '<my secure password>'`
* Replace `<host name or IP>` with the host name or IP of the server
* Replace `<port>` with the value of `RemoteMount.ApiPort` used in the server configuration
* Example:
* `repertory -rm 192.168.1.10:20000 -set RemoteConfig.EncryptionToken '<my secure password>'`
* `repertory -rm my.host.com:20000 -set RemoteConfig.EncryptionToken '<my secure password>'`
#### Client Remote Mounting
* Linux:
* `repertory -rm <host name or IP>:<port> /mnt/location`
* Example:
* `repertory -rm 192.168.1.10:20000 /mnt/location`
* Windows:
* `repertory -rm <host name or IP>:<port> t:`
* Example:
* `repertory -rm 192.168.1.10:20000 t:`
#### Remote Mount Configuration File
```json
{
"ApiPassword": "<random generated rpc password>",
"ApiPort": 10010,
"ApiUser": "repertory",
"EnableDriveEvents": false,
"EventLevel": "info",
"RemoteConfig": {
"ApiPort": 20000,
"EncryptionToken": "<my secure password>",
"HostNameOrIp": "192.168.1.10",
"MaxConnections": 20,
"ReceiveTimeoutMs": 120000,
"SendTimeoutMs": 30000
},
"TaskWaitMs": 100,
"Version": 1
}
```
## Compiling ## Compiling
* Successful compilation will result in all required files being placed in the `dist/` directory Successful compilation will result in all files required for execution to be placed
* Linux in the `dist/` directory
* Ensure `docker` is installed
### Linux Compilation
* Ensure `docker` is installed
* For x86_64: * For x86_64:
* RelWithDebInfo: `scripts/make_unix.sh` * RelWithDebInfo: `scripts/make_unix.sh`
* Release: `scripts/make_unix.sh x86_64 Release` * Release: `scripts/make_unix.sh x86_64 Release`
* Debug: `scripts/make_unix.sh x86_64 Debug` * Debug: `scripts/make_unix.sh x86_64 Debug`
* For aarch64: * For aarch64:
* RelWithDebInfo: `scripts/make_unix.sh aarch64` * RelWithDebInfo: `scripts/make_unix.sh aarch64`
* Release: `scripts/make_unix.sh aarch64 Release` * Release: `scripts/make_unix.sh aarch64 Release`
* Debug: `scripts/make_unix.sh aarch64 Debug` * Debug: `scripts/make_unix.sh aarch64 Debug`
* Windows
* OFFICIAL: Cross-compiling on Linux ### Windows Compilation
* OFFICIAL: Cross-compiling on Linux
* Ensure `docker` is installed * Ensure `docker` is installed
* RelWithDebInfo: `scripts/make_win32.sh` * RelWithDebInfo: `scripts/make_win32.sh`
* Release: `scripts/make_win32.sh x86_64 Release` * Release: `scripts/make_win32.sh x86_64 Release`
* Debug: `scripts/make_win32.sh x86_64 Debug` * Debug: `scripts/make_win32.sh x86_64 Debug`
* UNOFFICIAL: Compiling on Windows
* UNOFFICIAL: Compiling on Windows
* Ensure latest [MSYS2](https://www.msys2.org/) is installed * Ensure latest [MSYS2](https://www.msys2.org/) is installed
* RelWithDebInfo: `scripts\make_win32.cmd` * RelWithDebInfo: `scripts\make_win32.cmd`
* Release: `scripts\make_win32.cmd x86_64 Release` * Release: `scripts\make_win32.cmd x86_64 Release`

View File

@ -1,20 +1,20 @@
set(BINUTILS_HASH b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365) set(BINUTILS_HASH b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365)
set(BOOST2_HASH 7bd7ddceec1a1dfdcbdb3e609b60d01739c38390a5f956385a12f3122049f0ca) set(BOOST2_HASH 7bd7ddceec1a1dfdcbdb3e609b60d01739c38390a5f956385a12f3122049f0ca)
set(BOOST_HASH f55c340aa49763b1925ccf02b2e83f35fdcf634c9d5164a2acb87540173c741d) set(BOOST_HASH f55c340aa49763b1925ccf02b2e83f35fdcf634c9d5164a2acb87540173c741d)
set(CPP_HTTPLIB_HASH 405abd8170f2a446fc8612ac635d0db5947c0d2e156e32603403a4496255ff00) set(CPP_HTTPLIB_HASH c9b9e0524666e1cd088f0874c57c1ce7c0eaa8552f9f4e15c755d5201fc8c608)
set(CURL_HASH 5a231145114589491fc52da118f9c7ef8abee885d1cb1ced99c7290e9a352f07) set(CURL_HASH 6edc063d1ebaf9cf3b3b46e9fef2f3cd8932694989ecd43d005d6e828426d09f)
set(EXPAT_HASH 372b18f6527d162fa9658f1c74d22a37429b82d822f5a1e1fc7e00f6045a06a2) set(EXPAT_HASH 372b18f6527d162fa9658f1c74d22a37429b82d822f5a1e1fc7e00f6045a06a2)
set(GCC_HASH 7d376d445f93126dc545e2c0086d0f647c3094aae081cdb78f42ce2bc25e7293) set(GCC_HASH 7d376d445f93126dc545e2c0086d0f647c3094aae081cdb78f42ce2bc25e7293)
set(GTEST_HASH 7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926) set(GTEST_HASH 78c676fc63881529bf97bf9d45948d905a66833fbfa5318ea2cd7478cb98f399)
set(ICU_HASH a2c443404f00098e9e90acf29dc318e049d2dc78d9ae5f46efb261934a730ce2) set(ICU_HASH a2c443404f00098e9e90acf29dc318e049d2dc78d9ae5f46efb261934a730ce2)
set(JSON_HASH 0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406) set(JSON_HASH 0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406)
set(LIBSODIUM_HASH 8e5aeca07a723a27bbecc3beef14b0068d37e7fc0e97f51b3f1c82d2a58005c1) set(LIBSODIUM_HASH 8e5aeca07a723a27bbecc3beef14b0068d37e7fc0e97f51b3f1c82d2a58005c1)
set(MINGW_HASH 30e5aad2c48dd318150f79cff47661232c4175876d6b4d6b270961cf2b49a48b) set(MINGW_HASH cc41898aac4b6e8dd5cffd7331b9d9515b912df4420a3a612b5ea2955bbeed2f)
set(OPENSSL_HASH e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf) set(OPENSSL_HASH 002a2d6b30b58bf4bea46c43bdd96365aaf8daa6c428782aa4feee06da197df3)
set(PKG_CONFIG_HASH 6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591) set(PKG_CONFIG_HASH 6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591)
set(PUGIXML_HASH 2f10e276870c64b1db6809050a75e11a897a8d7456c4be5c6b2e35a11168a015) set(PUGIXML_HASH 655ade57fa703fb421c2eb9a0113b5064bddb145d415dd1f88c79353d90d511a)
set(ROCKSDB_HASH 9b810c81731835fda0d4bbdb51d3199d901fa4395733ab63752d297da84c5a47) set(ROCKSDB_HASH fdccab16133c9d927a183c2648bcea8d956fb41eb1df2aacaa73eb0b95e43724)
set(SPDLOG_HASH 9962648c9b4f1a7bbc76fd8d9172555bad1871fdb14ff4f842ef87949682caa5) set(SPDLOG_HASH 25c843860f039a1600f232c6eb9e01e6627f7d030a2ae5e232bdd3c9205d26cc)
set(SQLITE_HASH 77823cb110929c2bcb0f5d48e4833b5c59a8a6e40cdea3936b99e199dbbe5784) set(SQLITE_HASH 6cebd1d8403fc58c30e93939b246f3e6e58d0765a5cd50546f16c00fd805d2c3)
set(STDUUID_HASH b1176597e789531c38481acbbed2a6894ad419aab0979c10410d59eb0ebf40d3) set(STDUUID_HASH b1176597e789531c38481acbbed2a6894ad419aab0979c10410d59eb0ebf40d3)
set(ZLIB_HASH 17e88863f3600672ab49182f217281b6fc4d3c762bde361935e436a95214d05c) set(ZLIB_HASH 17e88863f3600672ab49182f217281b6fc4d3c762bde361935e436a95214d05c)

View File

@ -120,11 +120,11 @@ if(PROJECT_ENABLE_BOOST)
--with-libraries=atomic,chrono,date_time,filesystem,iostreams,locale,log,program_options,random,regex,serialization,system,test,thread --with-libraries=atomic,chrono,date_time,filesystem,iostreams,locale,log,program_options,random,regex,serialization,system,test,thread
BUILD_COMMAND BUILD_COMMAND
./b2 ./b2
-j1 -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
${BOOST_BUILD_ARGS} ${BOOST_BUILD_ARGS}
INSTALL_COMMAND INSTALL_COMMAND
./b2 ./b2
-j1 -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
${BOOST_BUILD_ARGS} ${BOOST_BUILD_ARGS}
install install
) )

View File

@ -15,10 +15,13 @@ if(PROJECT_ENABLE_CPP_HTTPLIB)
CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS} CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS}
-DBUILD_SHARED_LIBS=${PROJECT_BUILD_SHARED_LIBS} -DBUILD_SHARED_LIBS=${PROJECT_BUILD_SHARED_LIBS}
-DBUILD_STATIC_LIBS=ON -DBUILD_STATIC_LIBS=ON
-DHTTPLIB_REQUIRE_OPENSSL=${PROJECT_ENABLE_OPENSSL}
-DHTTPLIB_REQUIRE_ZLIB=ON
-DHTTPLIB_REQUIRE_BROTLI=OFF -DHTTPLIB_REQUIRE_BROTLI=OFF
-DHTTPLIB_REQUIRE_OPENSSL=ON
-DHTTPLIB_REQUIRE_ZLIB=ON
-DHTTPLIB_TEST=OFF -DHTTPLIB_TEST=OFF
-DHTTPLIB_USE_BROTLI_IF_AVAILABLE=OFF
-DHTTPLIB_USE_OPENSSL_IF_AVAILABLE=ON
-DHTTPLIB_USE_ZLIB_IF_AVAILABLE=ON
-DOPENSSL_USE_STATIC_LIBS=${OPENSSL_USE_STATIC_LIBS} -DOPENSSL_USE_STATIC_LIBS=${OPENSSL_USE_STATIC_LIBS}
) )

View File

@ -18,8 +18,9 @@ if(PROJECT_ENABLE_CURL)
URL ${PROJECT_3RD_PARTY_DIR}/curl-${CURL_VERSION}.tar.gz URL ${PROJECT_3RD_PARTY_DIR}/curl-${CURL_VERSION}.tar.gz
URL_HASH SHA256=${CURL_HASH} URL_HASH SHA256=${CURL_HASH}
LIST_SEPARATOR | LIST_SEPARATOR |
CMAKE_ARGS BUILD_COMMAND
${PROJECT_EXTERNAL_CMAKE_FLAGS} ${CMAKE_COMMAND} --build . -- -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS}
-DBUILD_CURL_EXE=OFF -DBUILD_CURL_EXE=OFF
-DBUILD_LIBCURL_DOCS=OFF -DBUILD_LIBCURL_DOCS=OFF
-DBUILD_MISC_DOCS=OFF -DBUILD_MISC_DOCS=OFF
@ -28,6 +29,7 @@ if(PROJECT_ENABLE_CURL)
-DBUILD_STATIC_LIBS=ON -DBUILD_STATIC_LIBS=ON
-DBUILD_STATIC_LIBS=ON -DBUILD_STATIC_LIBS=ON
-DBUILD_TESTING=OFF -DBUILD_TESTING=OFF
-DCURL_BROTLI=OFF
-DCURL_CA_BUNDLE=./cacert.pem -DCURL_CA_BUNDLE=./cacert.pem
-DCURL_CA_FALLBACK=ON -DCURL_CA_FALLBACK=ON
-DCURL_DISABLE_LDAP=ON -DCURL_DISABLE_LDAP=ON

View File

@ -9,6 +9,8 @@ if(PROJECT_ENABLE_ROCKSDB)
URL ${PROJECT_3RD_PARTY_DIR}/rocksdb-${ROCKSDB_VERSION}.tar.gz URL ${PROJECT_3RD_PARTY_DIR}/rocksdb-${ROCKSDB_VERSION}.tar.gz
URL_HASH SHA256=${ROCKSDB_HASH} URL_HASH SHA256=${ROCKSDB_HASH}
LIST_SEPARATOR | LIST_SEPARATOR |
BUILD_COMMAND
${CMAKE_COMMAND} --build . -- -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS} CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS}
-DBUILD_SHARED_LIBS=OFF -DBUILD_SHARED_LIBS=OFF
-DBUILD_STATIC_LIBS=ON -DBUILD_STATIC_LIBS=ON

View File

@ -2,7 +2,7 @@ if(PROJECT_ENABLE_SQLITE)
if(PROJECT_BUILD) if(PROJECT_BUILD)
add_definitions(-DPROJECT_ENABLE_SQLITE) add_definitions(-DPROJECT_ENABLE_SQLITE)
if (PROJECT_IS_MINGW AND NOT PROJECT_IS_MINGW_UNIX) if (PROJECT_IS_MINGW AND NOT PROJECT_IS_MINGW_UNIX)
pkg_check_modules(SQLITE3 REQUIRED sqlite3>=${SQLITE2_VERSION}) pkg_check_modules(SQLITE3 REQUIRED sqlite3)
include_directories(SYSTEM BEFORE ${SQLITE3_INCLUDE_DIRS}) include_directories(SYSTEM BEFORE ${SQLITE3_INCLUDE_DIRS})
link_libraries(${SQLITE3_LIBRARIES}) link_libraries(${SQLITE3_LIBRARIES})
else() else()

View File

@ -5,24 +5,24 @@ set(BOOST2_PATCH_VERSION 0)
set(BOOST_MAJOR_VERSION 1) set(BOOST_MAJOR_VERSION 1)
set(BOOST_MINOR_VERSION 87) set(BOOST_MINOR_VERSION 87)
set(BOOST_PATCH_VERSION 0) set(BOOST_PATCH_VERSION 0)
set(CPP_HTTPLIB_VERSION 0.18.1) set(CPP_HTTPLIB_VERSION 0.19.0)
set(CURL2_VERSION 8_11_0) set(CURL2_VERSION 8_12_1)
set(CURL_VERSION 8.11.0) set(CURL_VERSION 8.12.1)
set(EXPAT2_VERSION 2_6_4) set(EXPAT2_VERSION 2_6_4)
set(EXPAT_VERSION 2.6.4) set(EXPAT_VERSION 2.6.4)
set(GCC_VERSION 14.2.0) set(GCC_VERSION 14.2.0)
set(GTEST_VERSION 1.15.2) set(GTEST_VERSION 1.16.0)
set(ICU_VERSION 76-1) set(ICU_VERSION 76-1)
set(JSON_VERSION 3.11.3) set(JSON_VERSION 3.11.3)
set(LIBSODIUM_VERSION 1.0.20) set(LIBSODIUM_VERSION 1.0.20)
set(MESA_VERSION 23.3.3) set(MESA_VERSION 23.3.3)
set(MINGW_VERSION 12.0.0) set(MINGW_VERSION 12.0.0)
set(OPENSSL_VERSION 3.4.0) set(OPENSSL_VERSION 3.4.1)
set(PKG_CONFIG_VERSION 0.29.2) set(PKG_CONFIG_VERSION 0.29.2)
set(PUGIXML_VERSION 1.14) set(PUGIXML_VERSION 1.15)
set(ROCKSDB_VERSION 9.7.4) set(ROCKSDB_VERSION 9.10.0)
set(SPDLOG_VERSION 1.15.0) set(SPDLOG_VERSION 1.15.1)
set(SQLITE2_VERSION 3.46.1) set(SQLITE2_VERSION 3.49.1)
set(SQLITE_VERSION 3460100) set(SQLITE_VERSION 3490100)
set(STDUUID_VERSION 1.2.3) set(STDUUID_VERSION 1.2.3)
set(ZLIB_VERSION 1.3.1) set(ZLIB_VERSION 1.3.1)

View File

@ -10,7 +10,7 @@ PROJECT_DESC="Mount utility for Sia and S3"
PROJECT_MAJOR_VERSION=2 PROJECT_MAJOR_VERSION=2
PROJECT_MINOR_VERSION=0 PROJECT_MINOR_VERSION=0
PROJECT_REVISION_VERSION=4 PROJECT_REVISION_VERSION=5
PROJECT_RELEASE_NUM=0 PROJECT_RELEASE_NUM=0
PROJECT_RELEASE_ITER=rc PROJECT_RELEASE_ITER=rc
@ -19,6 +19,8 @@ PROJECT_APP_LIST=(${PROJECT_NAME})
PROJECT_PRIVATE_KEY=${DEVELOPER_PRIVATE_KEY} PROJECT_PRIVATE_KEY=${DEVELOPER_PRIVATE_KEY}
PROJECT_PUBLIC_KEY=${DEVELOPER_PUBLIC_KEY} PROJECT_PUBLIC_KEY=${DEVELOPER_PUBLIC_KEY}
PROJECT_FLUTTER_BASE_HREF="/ui/"
PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF
PROJECT_ENABLE_BACKWARD_CPP=OFF PROJECT_ENABLE_BACKWARD_CPP=OFF

View File

@ -31,6 +31,7 @@ RUN apk add \
gflags \ gflags \
gflags-dev \ gflags-dev \
git \ git \
git-lfs \
icu-dev \ icu-dev \
icu-libs \ icu-libs \
icu-static \ icu-static \

View File

@ -31,6 +31,7 @@ RUN apk add \
gflags \ gflags \
gflags-dev \ gflags-dev \
git \ git \
git-lfs \
icu-dev \ icu-dev \
icu-libs \ icu-libs \
icu-static \ icu-static \

48
docker/x86_64/flutter Normal file
View File

@ -0,0 +1,48 @@
FROM debian:latest
ARG UID=0
ARG GID=0
RUN apt-get update
RUN apt-get install -y \
bash \
curl \
fonts-droid-fallback \
gdb \
git \
lib32stdc++6 \
libgconf-2-4 \
libglu1-mesa \
libstdc++6 \
python3 \
unzip \
wget
RUN apt-get clean
RUN git clone https://github.com/flutter/flutter.git /flutter
RUN git config --system --add safe.directory /flutter
ENV PATH="/flutter/bin:/flutter/bin/cache/dart-sdk/bin:${PATH}"
RUN flutter doctor -v
RUN flutter channel master
RUN flutter upgrade
RUN flutter --disable-analytics
RUN flutter config --no-analytics
RUN flutter config --enable-web
RUN flutter config --no-cli-animations
RUN mkdir /nonexistent
RUN chown -R $UID:$GID /nonexistent
RUN mkdir /.config
RUN chown -R $UID:$GID /.config
RUN mkdir /.dart-tool
RUN chown -R $UID:$GID /.dart-tool
RUN mkdir /.pub-cache
RUN chown -R $UID:$GID /.pub-cache
RUN chown -R $UID:$GID /flutter

View File

@ -18,6 +18,7 @@ RUN apk add \
gcc \ gcc \
gettext \ gettext \
git \ git \
git-lfs \
gmp \ gmp \
gmp-dev \ gmp-dev \
gperf \ gperf \
@ -679,6 +680,7 @@ RUN if [ -f "/3rd_party/curl-${MY_CURL_VERSION}.tar.gz" ]; then \
-DCMAKE_CXX_STANDARD=${MY_CXX_STANDARD} \ -DCMAKE_CXX_STANDARD=${MY_CXX_STANDARD} \
-DCMAKE_INSTALL_PREFIX=${MY_MINGW_DIR} \ -DCMAKE_INSTALL_PREFIX=${MY_MINGW_DIR} \
-DCMAKE_TOOLCHAIN_FILE=${MY_TOOLCHAIN_FILE_CMAKE} \ -DCMAKE_TOOLCHAIN_FILE=${MY_TOOLCHAIN_FILE_CMAKE} \
-DCURL_BROTLI=OFF \
-DCURL_CA_BUNDLE=./cacert.pem \ -DCURL_CA_BUNDLE=./cacert.pem \
-DCURL_CA_FALLBACK=ON \ -DCURL_CA_FALLBACK=ON \
-DCURL_DISABLE_LDAP=ON \ -DCURL_DISABLE_LDAP=ON \
@ -714,6 +716,9 @@ RUN if [ -f "/3rd_party/cpp-httplib-${MY_CPP_HTTPLIB_VERSION}.tar.gz" ]; then \
-DHTTPLIB_REQUIRE_OPENSSL=ON \ -DHTTPLIB_REQUIRE_OPENSSL=ON \
-DHTTPLIB_REQUIRE_ZLIB=ON \ -DHTTPLIB_REQUIRE_ZLIB=ON \
-DHTTPLIB_TEST=OFF \ -DHTTPLIB_TEST=OFF \
-DHTTPLIB_USE_BROTLI_IF_AVAILABLE=OFF \
-DHTTPLIB_USE_OPENSSL_IF_AVAILABLE=YES \
-DHTTPLIB_USE_ZLIB_IF_AVAILABLE=ON \
&& make -j${MY_NUM_JOBS} \ && make -j${MY_NUM_JOBS} \
&& make install \ && make install \
&& cd ${MY_WORKDIR} \ && cd ${MY_WORKDIR} \

View File

@ -43,8 +43,7 @@ public:
[[nodiscard]] static auto default_remote_api_port(const provider_type &prov) [[nodiscard]] static auto default_remote_api_port(const provider_type &prov)
-> std::uint16_t; -> std::uint16_t;
[[nodiscard]] static auto default_rpc_port(const provider_type &prov) [[nodiscard]] static auto default_rpc_port() -> std::uint16_t;
-> std::uint16_t;
[[nodiscard]] static auto get_provider_display_name(const provider_type &prov) [[nodiscard]] static auto get_provider_display_name(const provider_type &prov)
-> std::string; -> std::string;
@ -52,6 +51,8 @@ public:
[[nodiscard]] static auto get_provider_name(const provider_type &prov) [[nodiscard]] static auto get_provider_name(const provider_type &prov)
-> std::string; -> std::string;
[[nodiscard]] static auto get_root_data_directory() -> std::string;
public: public:
[[nodiscard]] static auto get_stop_requested() -> bool; [[nodiscard]] static auto get_stop_requested() -> bool;
@ -71,7 +72,7 @@ public:
private: private:
provider_type prov_; provider_type prov_;
atomic<std::string> api_auth_; atomic<std::string> api_password_;
std::atomic<std::uint16_t> api_port_; std::atomic<std::uint16_t> api_port_;
atomic<std::string> api_user_; atomic<std::string> api_user_;
std::atomic<bool> config_changed_; std::atomic<bool> config_changed_;
@ -121,7 +122,7 @@ private:
auto set_value(dest &dst, const source &src) -> bool; auto set_value(dest &dst, const source &src) -> bool;
public: public:
[[nodiscard]] auto get_api_auth() const -> std::string; [[nodiscard]] auto get_api_password() const -> std::string;
[[nodiscard]] auto get_api_port() const -> std::uint16_t; [[nodiscard]] auto get_api_port() const -> std::uint16_t;
@ -199,7 +200,7 @@ public:
void save(); void save();
void set_api_auth(const std::string &value); void set_api_password(const std::string &value);
void set_api_port(std::uint16_t value); void set_api_port(std::uint16_t value);

View File

@ -57,7 +57,7 @@ using json = nlohmann::json;
inline constexpr const std::string_view REPERTORY = "repertory"; inline constexpr const std::string_view REPERTORY = "repertory";
inline constexpr const std::wstring_view REPERTORY_W = L"repertory"; inline constexpr const std::wstring_view REPERTORY_W = L"repertory";
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION = 1ULL; inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION = 2ULL;
inline constexpr const std::string_view REPERTORY_DATA_NAME = "repertory2"; inline constexpr const std::string_view REPERTORY_DATA_NAME = "repertory2";
inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION = "2.0.0"; inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION = "2.0.0";

View File

@ -41,7 +41,7 @@ private:
const std::string unique_id_; const std::string unique_id_;
const std::string mutex_id_; const std::string mutex_id_;
int lock_fd_; int lock_fd_;
int lock_status_ = EWOULDBLOCK; int lock_status_{EWOULDBLOCK};
private: private:
[[nodiscard]] static auto get_state_directory() -> std::string; [[nodiscard]] static auto get_state_directory() -> std::string;
@ -51,17 +51,20 @@ private:
[[nodiscard]] auto get_lock_file() -> std::string; [[nodiscard]] auto get_lock_file() -> std::string;
private: private:
[[nodiscard]] static auto [[nodiscard]] static auto wait_for_lock(int fd,
wait_for_lock(int fd, std::uint8_t retry_count = 30u) -> int; std::uint8_t retry_count = 30u)
-> int;
public: public:
[[nodiscard]] auto get_mount_state(json &mount_state) -> bool; [[nodiscard]] auto get_mount_state(json &mount_state) -> bool;
[[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30u) -> lock_result; [[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30u) -> lock_result;
void release();
[[nodiscard]] auto set_mount_state(bool active, [[nodiscard]] auto set_mount_state(bool active,
const std::string &mount_location, const std::string &mount_location, int pid)
int pid) -> bool; -> bool;
}; };
[[nodiscard]] auto create_meta_attributes( [[nodiscard]] auto create_meta_attributes(

View File

@ -48,9 +48,9 @@ public:
[[nodiscard]] auto get_pinned_files() -> rpc_response; [[nodiscard]] auto get_pinned_files() -> rpc_response;
[[nodiscard]] auto pin_file(const std::string &api_file) -> rpc_response; [[nodiscard]] auto pin_file(const std::string &api_path) -> rpc_response;
[[nodiscard]] auto pinned_status(const std::string &api_file) -> rpc_response; [[nodiscard]] auto pinned_status(const std::string &api_path) -> rpc_response;
[[nodiscard]] auto set_config_value_by_name(const std::string &name, [[nodiscard]] auto set_config_value_by_name(const std::string &name,
const std::string &value) const std::string &value)
@ -58,7 +58,7 @@ public:
[[nodiscard]] auto unmount() -> rpc_response; [[nodiscard]] auto unmount() -> rpc_response;
[[nodiscard]] auto unpin_file(const std::string &api_file) -> rpc_response; [[nodiscard]] auto unpin_file(const std::string &api_path) -> rpc_response;
}; };
} // namespace repertory } // namespace repertory

View File

@ -0,0 +1,82 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#ifndef REPERTORY_INCLUDE_RPC_COMMON_HPP_
#define REPERTORY_INCLUDE_RPC_COMMON_HPP_
#include "utils/base64.hpp"
#include "utils/error_utils.hpp"
#include "utils/string.hpp"
namespace repertory::rpc {
[[nodiscard]] auto check_authorization(const auto &cfg,
const httplib::Request &req) -> bool {
REPERTORY_USES_FUNCTION_NAME();
if (cfg.get_api_password().empty() || cfg.get_api_user().empty()) {
utils::error::raise_error(function_name,
"authorization user or password is not set");
return false;
}
auto authorization = req.get_header_value("Authorization");
if (authorization.empty()) {
utils::error::raise_error(function_name,
"'Authorization' header is not set");
return false;
}
auto auth_parts = utils::string::split(authorization, ' ', true);
if (auth_parts.empty()) {
utils::error::raise_error(function_name, "'Authorization' header is empty");
return false;
}
auto auth_type = auth_parts[0U];
if (auth_type != "Basic") {
utils::error::raise_error(function_name,
"authorization type is not 'Basic'");
return false;
}
auto data = macaron::Base64::Decode(authorization.substr(6U));
auto auth_str = std::string(data.begin(), data.end());
auto auth = utils::string::split(auth_str, ':', false);
if (auth.size() < 2U) {
utils::error::raise_error(function_name, "authorization data is not valid");
return false;
}
auto user = auth.at(0U);
auth.erase(auth.begin());
auto pwd = utils::string::join(auth, ':');
if ((user != cfg.get_api_user()) || (pwd != cfg.get_api_password())) {
utils::error::raise_error(function_name, "authorization failed");
return false;
}
return true;
}
} // namespace repertory::rpc
#endif // REPERTORY_INCLUDE_RPC_COMMON_HPP_

View File

@ -40,8 +40,6 @@ private:
std::mutex start_stop_mutex_; std::mutex start_stop_mutex_;
private: private:
[[nodiscard]] auto check_authorization(const httplib::Request &req) -> bool;
void handle_get_config(const httplib::Request &req, httplib::Response &res); void handle_get_config(const httplib::Request &req, httplib::Response &res);
void handle_get_config_value_by_name(const httplib::Request &req, void handle_get_config_value_by_name(const httplib::Request &req,

View File

@ -23,7 +23,7 @@
#define REPERTORY_INCLUDE_TYPES_REPERTORY_HPP_ #define REPERTORY_INCLUDE_TYPES_REPERTORY_HPP_
namespace repertory { namespace repertory {
constexpr const auto default_api_auth_size{48U}; constexpr const auto default_api_password_size{48U};
constexpr const auto default_download_timeout_secs{30U}; constexpr const auto default_download_timeout_secs{30U};
constexpr const auto default_eviction_delay_mins{1U}; constexpr const auto default_eviction_delay_mins{1U};
constexpr const auto default_high_freq_interval_secs{std::uint16_t{30U}}; constexpr const auto default_high_freq_interval_secs{std::uint16_t{30U}};
@ -38,6 +38,7 @@ constexpr const auto default_retry_read_count{6U};
constexpr const auto default_ring_buffer_file_size{512U}; constexpr const auto default_ring_buffer_file_size{512U};
constexpr const auto default_task_wait_ms{100U}; constexpr const auto default_task_wait_ms{100U};
constexpr const auto default_timeout_ms{60000U}; constexpr const auto default_timeout_ms{60000U};
constexpr const auto default_ui_mgmt_port{std::uint16_t{30000U}};
constexpr const auto max_ring_buffer_file_size{std::uint16_t(1024U)}; constexpr const auto max_ring_buffer_file_size{std::uint16_t(1024U)};
constexpr const auto max_s3_object_name_length{1024U}; constexpr const auto max_s3_object_name_length{1024U};
constexpr const auto min_cache_size_bytes{ constexpr const auto min_cache_size_bytes{
@ -280,6 +281,8 @@ enum class exit_code : std::int32_t {
pin_failed = -16, pin_failed = -16,
unpin_failed = -17, unpin_failed = -17,
init_failed = -18, init_failed = -18,
ui_mount_failed = -19,
exception = -20,
}; };
enum http_error_codes : std::int32_t { enum http_error_codes : std::int32_t {
@ -304,6 +307,18 @@ enum class provider_type : std::size_t {
unknown, unknown,
}; };
[[nodiscard]] auto
provider_type_from_string(std::string_view type,
provider_type default_type = provider_type::unknown)
-> provider_type;
[[nodiscard]] auto provider_type_to_string(provider_type type) -> std::string;
void clean_json_config(provider_type prov, nlohmann::json &data);
[[nodiscard]] auto clean_json_value(std::string_view name,
std::string_view data) -> std::string;
#if defined(_WIN32) #if defined(_WIN32)
struct open_file_data final { struct open_file_data final {
PVOID directory_buffer{nullptr}; PVOID directory_buffer{nullptr};
@ -452,7 +467,6 @@ using meta_provider_callback = std::function<void(directory_item &)>;
inline constexpr const auto JSON_ACCESS_KEY{"AccessKey"}; inline constexpr const auto JSON_ACCESS_KEY{"AccessKey"};
inline constexpr const auto JSON_AGENT_STRING{"AgentString"}; inline constexpr const auto JSON_AGENT_STRING{"AgentString"};
inline constexpr const auto JSON_API_AUTH{"ApiAuth"};
inline constexpr const auto JSON_API_PARENT{"ApiParent"}; inline constexpr const auto JSON_API_PARENT{"ApiParent"};
inline constexpr const auto JSON_API_PASSWORD{"ApiPassword"}; inline constexpr const auto JSON_API_PASSWORD{"ApiPassword"};
inline constexpr const auto JSON_API_PATH{"ApiPath"}; inline constexpr const auto JSON_API_PATH{"ApiPath"};
@ -487,6 +501,7 @@ inline constexpr const auto JSON_MAX_UPLOAD_COUNT{"MaxUploadCount"};
inline constexpr const auto JSON_MED_FREQ_INTERVAL_SECS{ inline constexpr const auto JSON_MED_FREQ_INTERVAL_SECS{
"MedFreqIntervalSeconds"}; "MedFreqIntervalSeconds"};
inline constexpr const auto JSON_META{"Meta"}; inline constexpr const auto JSON_META{"Meta"};
inline constexpr const auto JSON_MOUNT_LOCATIONS{"MountLocations"};
inline constexpr const auto JSON_ONLINE_CHECK_RETRY_SECS{ inline constexpr const auto JSON_ONLINE_CHECK_RETRY_SECS{
"OnlineCheckRetrySeconds"}; "OnlineCheckRetrySeconds"};
inline constexpr const auto JSON_PATH{"Path"}; inline constexpr const auto JSON_PATH{"Path"};

View File

@ -49,6 +49,8 @@ static const option password_option = {"-pw", "--password"};
static const option remote_mount_option = {"-rm", "--remote_mount"}; static const option remote_mount_option = {"-rm", "--remote_mount"};
static const option set_option = {"-set", "--set"}; static const option set_option = {"-set", "--set"};
static const option status_option = {"-status", "--status"}; static const option status_option = {"-status", "--status"};
static const option ui_option = {"-ui", "--ui"};
static const option ui_port_option = {"-up", "--ui_port"};
static const option unmount_option = {"-unmount", "--unmount"}; static const option unmount_option = {"-unmount", "--unmount"};
static const option unpin_file_option = {"-uf", "--unpin_file"}; static const option unpin_file_option = {"-uf", "--unpin_file"};
static const option user_option = {"-us", "--user"}; static const option user_option = {"-us", "--user"};
@ -75,6 +77,8 @@ static const std::vector<option> option_list = {
remote_mount_option, remote_mount_option,
set_option, set_option,
status_option, status_option,
ui_option,
ui_port_option,
unmount_option, unmount_option,
unpin_file_option, unpin_file_option,
user_option, user_option,
@ -87,26 +91,27 @@ void get_api_authentication_data(std::string &user, std::string &password,
std::uint16_t &port, const provider_type &prov, std::uint16_t &port, const provider_type &prov,
const std::string &data_directory); const std::string &data_directory);
[[nodiscard]] auto [[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)
get_provider_type_from_args(std::vector<const char *> args) -> provider_type; -> provider_type;
[[nodiscard]] auto has_option(std::vector<const char *> args, [[nodiscard]] auto has_option(std::vector<const char *> args,
const std::string &option_name) -> bool; const std::string &option_name) -> bool;
[[nodiscard]] auto has_option(std::vector<const char *> args, [[nodiscard]] auto has_option(std::vector<const char *> args, const option &opt)
const option &opt) -> bool; -> bool;
[[nodiscard]] auto parse_option(std::vector<const char *> args, [[nodiscard]] auto parse_option(std::vector<const char *> args,
const std::string &option_name, const std::string &option_name,
std::uint8_t count) -> std::vector<std::string>; std::uint8_t count) -> std::vector<std::string>;
[[nodiscard]] auto parse_string_option(std::vector<const char *> args, [[nodiscard]] auto parse_string_option(std::vector<const char *> args,
const option &opt, const option &opt, std::string &value)
std::string &value) -> exit_code; -> exit_code;
[[nodiscard]] auto [[nodiscard]] auto parse_drive_options(std::vector<const char *> args,
parse_drive_options(std::vector<const char *> args, provider_type &prov, provider_type &prov,
std::string &data_directory) -> std::vector<std::string>; std::string &data_directory)
-> std::vector<std::string>;
} // namespace repertory::utils::cli } // namespace repertory::utils::cli
#endif // REPERTORY_INCLUDE_UTILS_CLI_UTILS_HPP_ #endif // REPERTORY_INCLUDE_UTILS_CLI_UTILS_HPP_

View File

@ -66,8 +66,8 @@ void app_config::set_stop_requested() { stop_requested.store(true); }
app_config::app_config(const provider_type &prov, app_config::app_config(const provider_type &prov,
std::string_view data_directory) std::string_view data_directory)
: prov_(prov), : prov_(prov),
api_auth_(utils::generate_random_string(default_api_auth_size)), api_password_(utils::generate_random_string(default_api_password_size)),
api_port_(default_rpc_port(prov)), api_port_(default_rpc_port()),
api_user_(std::string{REPERTORY}), api_user_(std::string{REPERTORY}),
config_changed_(false), config_changed_(false),
download_timeout_secs_(default_download_timeout_secs), download_timeout_secs_(default_download_timeout_secs),
@ -124,7 +124,7 @@ app_config::app_config(const provider_type &prov,
} }
value_get_lookup_ = { value_get_lookup_ = {
{JSON_API_AUTH, [this]() { return get_api_auth(); }}, {JSON_API_PASSWORD, [this]() { return get_api_password(); }},
{JSON_API_PORT, [this]() { return std::to_string(get_api_port()); }}, {JSON_API_PORT, [this]() { return std::to_string(get_api_port()); }},
{JSON_API_USER, [this]() { return get_api_user(); }}, {JSON_API_USER, [this]() { return get_api_user(); }},
{JSON_DATABASE_TYPE, {JSON_DATABASE_TYPE,
@ -253,10 +253,10 @@ app_config::app_config(const provider_type &prov,
value_set_lookup_ = { value_set_lookup_ = {
{ {
JSON_API_AUTH, JSON_API_PASSWORD,
[this](const std::string &value) { [this](const std::string &value) {
set_api_auth(value); set_api_password(value);
return get_api_auth(); return get_api_password();
}, },
}, },
{ {
@ -699,36 +699,37 @@ auto app_config::default_api_port(const provider_type &prov) -> std::uint16_t {
return PROVIDER_API_PORTS.at(static_cast<std::size_t>(prov)); return PROVIDER_API_PORTS.at(static_cast<std::size_t>(prov));
} }
auto app_config::default_data_directory(const provider_type &prov) auto app_config::get_root_data_directory() -> std::string {
-> std::string {
#if defined(_WIN32) #if defined(_WIN32)
auto data_directory = auto data_directory = utils::path::combine(
utils::path::combine(utils::get_local_app_data_directory(), utils::get_local_app_data_directory(), {
{
REPERTORY_DATA_NAME, REPERTORY_DATA_NAME,
app_config::get_provider_name(prov),
}); });
#else // !defined(_WIN32) #else // !defined(_WIN32)
#if defined(__APPLE__) #if defined(__APPLE__)
auto data_directory = auto data_directory = utils::path::combine("~", {
utils::path::combine("~", {
"Library", "Library",
"Application Support", "Application Support",
REPERTORY_DATA_NAME, REPERTORY_DATA_NAME,
app_config::get_provider_name(prov),
}); });
#else // !defined(__APPLE__) #else // !defined(__APPLE__)
auto data_directory = auto data_directory = utils::path::combine("~", {
utils::path::combine("~", {
".local", ".local",
REPERTORY_DATA_NAME, REPERTORY_DATA_NAME,
app_config::get_provider_name(prov),
}); });
#endif // defined(__APPLE__) #endif // defined(__APPLE__)
#endif // defined(_WIN32) #endif // defined(_WIN32)
return data_directory; return data_directory;
} }
auto app_config::default_data_directory(const provider_type &prov)
-> std::string {
return utils::path::combine(app_config::get_root_data_directory(),
{
app_config::get_provider_name(prov),
});
}
auto app_config::default_remote_api_port(const provider_type &prov) auto app_config::default_remote_api_port(const provider_type &prov)
-> std::uint16_t { -> std::uint16_t {
static const std::array<std::uint16_t, static const std::array<std::uint16_t,
@ -741,19 +742,12 @@ auto app_config::default_remote_api_port(const provider_type &prov)
}; };
return PROVIDER_REMOTE_PORTS.at(static_cast<std::size_t>(prov)); return PROVIDER_REMOTE_PORTS.at(static_cast<std::size_t>(prov));
} }
auto app_config::default_rpc_port(const provider_type &prov) -> std::uint16_t {
static const std::array<std::uint16_t,
static_cast<std::size_t>(provider_type::unknown)>
PROVIDER_RPC_PORTS = {
10000U,
10010U,
10100U,
10002U,
};
return PROVIDER_RPC_PORTS.at(static_cast<std::size_t>(prov));
}
auto app_config::get_api_auth() const -> std::string { return api_auth_; } auto app_config::default_rpc_port() -> std::uint16_t { return 10000U; }
auto app_config::get_api_password() const -> std::string {
return api_password_;
}
auto app_config::get_api_port() const -> std::uint16_t { return api_port_; } auto app_config::get_api_port() const -> std::uint16_t { return api_port_; }
@ -814,7 +808,7 @@ auto app_config::get_host_config() const -> host_config { return host_config_; }
auto app_config::get_json() const -> json { auto app_config::get_json() const -> json {
json ret = { json ret = {
{JSON_API_AUTH, api_auth_}, {JSON_API_PASSWORD, api_password_},
{JSON_API_PORT, api_port_}, {JSON_API_PORT, api_port_},
{JSON_API_USER, api_user_}, {JSON_API_USER, api_user_},
{JSON_DOWNLOAD_TIMEOUT_SECS, download_timeout_secs_}, {JSON_DOWNLOAD_TIMEOUT_SECS, download_timeout_secs_},
@ -939,24 +933,18 @@ auto app_config::get_preferred_download_type() const -> download_type {
auto app_config::get_provider_display_name(const provider_type &prov) auto app_config::get_provider_display_name(const provider_type &prov)
-> std::string { -> std::string {
static const std::array<std::string, static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown)> static_cast<std::size_t>(provider_type::unknown) + 1U>
PROVIDER_DISPLAY_NAMES = { PROVIDER_DISPLAY_NAMES = {
"Sia", "Sia", "Remote", "S3", "Encrypt", "Unknown",
"Remote",
"S3",
"Encrypt",
}; };
return PROVIDER_DISPLAY_NAMES.at(static_cast<std::size_t>(prov)); return PROVIDER_DISPLAY_NAMES.at(static_cast<std::size_t>(prov));
} }
auto app_config::get_provider_name(const provider_type &prov) -> std::string { auto app_config::get_provider_name(const provider_type &prov) -> std::string {
static const std::array<std::string, static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown)> static_cast<std::size_t>(provider_type::unknown) + 1U>
PROVIDER_NAMES = { PROVIDER_NAMES = {
"sia", "sia", "remote", "s3", "encrypt", "unknown",
"remote",
"s3",
"encrypt",
}; };
return PROVIDER_NAMES.at(static_cast<std::size_t>(prov)); return PROVIDER_NAMES.at(static_cast<std::size_t>(prov));
} }
@ -1035,7 +1023,7 @@ auto app_config::load() -> bool {
auto found{true}; auto found{true};
auto json_document = json::parse(json_text); auto json_document = json::parse(json_text);
get_value(json_document, JSON_API_AUTH, api_auth_, found); get_value(json_document, JSON_API_PASSWORD, api_password_, found);
get_value(json_document, JSON_API_PORT, api_port_, found); get_value(json_document, JSON_API_PORT, api_port_, found);
get_value(json_document, JSON_API_USER, api_user_, found); get_value(json_document, JSON_API_USER, api_user_, found);
get_value(json_document, JSON_DATABASE_TYPE, db_type_, found); get_value(json_document, JSON_DATABASE_TYPE, db_type_, found);
@ -1092,6 +1080,13 @@ auto app_config::load() -> bool {
set_value(max_cache_size_bytes_, default_max_cache_size_bytes); set_value(max_cache_size_bytes_, default_max_cache_size_bytes);
} }
} }
if (version_ == 2U) {
if (json_document.contains("ApiAuth")) {
api_password_ = json_document.at("ApiAuth").get<std::string>();
}
}
found = false; found = false;
} }
@ -1130,8 +1125,8 @@ void app_config::save() {
}); });
} }
void app_config::set_api_auth(const std::string &value) { void app_config::set_api_password(const std::string &value) {
set_value(api_auth_, value); set_value(api_password_, value);
} }
void app_config::set_api_port(std::uint16_t value) { void app_config::set_api_port(std::uint16_t value) {

View File

@ -47,16 +47,7 @@ lock_data::lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
lock_data::lock_data() lock_data::lock_data()
: pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {} : pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {}
lock_data::~lock_data() { lock_data::~lock_data() { release(); }
if (lock_fd_ != -1) {
if (lock_status_ == 0) {
unlink(get_lock_file().c_str());
flock(lock_fd_, LOCK_UN);
}
close(lock_fd_);
}
}
auto lock_data::get_lock_data_file() -> std::string { auto lock_data::get_lock_data_file() -> std::string {
const auto dir = get_state_directory(); const auto dir = get_state_directory();
@ -125,6 +116,20 @@ auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
} }
} }
void lock_data::release() {
if (lock_fd_ == -1) {
return;
}
if (lock_status_ == 0) {
unlink(get_lock_file().c_str());
flock(lock_fd_, LOCK_UN);
}
close(lock_fd_);
lock_fd_ = -1;
}
auto lock_data::set_mount_state(bool active, const std::string &mount_location, auto lock_data::set_mount_state(bool active, const std::string &mount_location,
int pid) -> bool { int pid) -> bool {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();

View File

@ -22,8 +22,6 @@
#include "rpc/client/client.hpp" #include "rpc/client/client.hpp"
#include "types/repertory.hpp" #include "types/repertory.hpp"
#include "utils/base64.hpp"
#include "utils/utils.hpp"
namespace repertory { namespace repertory {
client::client(rpc_host_info host_info) : host_info_(std::move(host_info)) {} client::client(rpc_host_info host_info) : host_info_(std::move(host_info)) {}

View File

@ -28,6 +28,7 @@
#include "events/types/service_stop_begin.hpp" #include "events/types/service_stop_begin.hpp"
#include "events/types/service_stop_end.hpp" #include "events/types/service_stop_end.hpp"
#include "events/types/unmount_requested.hpp" #include "events/types/unmount_requested.hpp"
#include "rpc/common.hpp"
#include "utils/base64.hpp" #include "utils/base64.hpp"
#include "utils/error_utils.hpp" #include "utils/error_utils.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
@ -35,57 +36,10 @@
namespace repertory { namespace repertory {
server::server(app_config &config) : config_(config) {} server::server(app_config &config) : config_(config) {}
auto server::check_authorization(const httplib::Request &req) -> bool {
REPERTORY_USES_FUNCTION_NAME();
if (config_.get_api_auth().empty() || config_.get_api_user().empty()) {
utils::error::raise_error(function_name,
"authorization user or password is not set");
return false;
}
auto authorization = req.get_header_value("Authorization");
if (authorization.empty()) {
utils::error::raise_error(function_name, "Authorization header is not set");
return false;
}
auto auth_parts = utils::string::split(authorization, ' ', true);
if (auth_parts.empty()) {
utils::error::raise_error(function_name, "Authorization header is empty");
return false;
}
auto auth_type = auth_parts[0U];
if (auth_type != "Basic") {
utils::error::raise_error(function_name, "Authorization is not Basic");
return false;
}
auto data = macaron::Base64::Decode(authorization.substr(6U));
auto auth_str = std::string(data.begin(), data.end());
auto auth = utils::string::split(auth_str, ':', false);
if (auth.size() < 2U) {
utils::error::raise_error(function_name, "Authorization is not valid");
return false;
}
auto user = auth.at(0U);
auth.erase(auth.begin());
auto pwd = utils::string::join(auth, ':');
if ((user != config_.get_api_user()) || (pwd != config_.get_api_auth())) {
utils::error::raise_error(function_name, "Authorization failed");
return false;
}
return true;
}
void server::handle_get_config(const httplib::Request & /*req*/, void server::handle_get_config(const httplib::Request & /*req*/,
httplib::Response &res) { httplib::Response &res) {
auto data = config_.get_json(); auto data = config_.get_json();
clean_json_config(config_.get_provider_type(), data);
res.set_content(data.dump(), "application/json"); res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok; res.status = http_error_codes::ok;
} }
@ -93,7 +47,10 @@ void server::handle_get_config(const httplib::Request & /*req*/,
void server::handle_get_config_value_by_name(const httplib::Request &req, void server::handle_get_config_value_by_name(const httplib::Request &req,
httplib::Response &res) { httplib::Response &res) {
auto name = req.get_param_value("name"); auto name = req.get_param_value("name");
auto data = json({{"value", config_.get_value_by_name(name)}}); auto data = json({{
"value",
clean_json_value(name, config_.get_value_by_name(name)),
}});
res.set_content(data.dump(), "application/json"); res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok; res.status = http_error_codes::ok;
} }
@ -103,7 +60,10 @@ void server::handle_set_config_value_by_name(const httplib::Request &req,
auto name = req.get_param_value("name"); auto name = req.get_param_value("name");
auto value = req.get_param_value("value"); auto value = req.get_param_value("value");
json data = {{"value", config_.set_value_by_name(name, value)}}; json data = {{
"value",
clean_json_value(name, config_.set_value_by_name(name, value)),
}};
res.set_content(data.dump(), "application/json"); res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok; res.status = http_error_codes::ok;
} }
@ -173,7 +133,7 @@ void server::start() {
server_->set_pre_routing_handler( server_->set_pre_routing_handler(
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse { [this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
if (check_authorization(req)) { if (rpc::check_authorization(config_, req)) {
return httplib::Server::HandlerResponse::Unhandled; return httplib::Server::HandlerResponse::Unhandled;
} }
@ -183,8 +143,17 @@ void server::start() {
initialize(*server_); initialize(*server_);
server_thread_ = std::make_unique<std::thread>( server_thread_ = std::make_unique<std::thread>([this]() {
[this]() { server_->listen("127.0.0.1", config_.get_api_port()); }); #ifdef _WIN32
server_->set_socket_options([](auto &&sock) {
int enable = 1;
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *>(&enable), sizeof(enable));
});
#endif // _WIN32
server_->listen("127.0.0.1", config_.get_api_port());
});
event_system::instance().raise<service_start_end>(function_name, "server"); event_system::instance().raise<service_start_end>(function_name, "server");
} }

View File

@ -21,10 +21,56 @@
*/ */
#include "types/repertory.hpp" #include "types/repertory.hpp"
#include "app_config.hpp"
#include "types/startup_exception.hpp" #include "types/startup_exception.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
namespace repertory { namespace repertory {
void clean_json_config(provider_type prov, nlohmann::json &data) {
data[JSON_API_PASSWORD] = "";
switch (prov) {
case provider_type::encrypt:
data[JSON_ENCRYPT_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
break;
case provider_type::remote:
data[JSON_REMOTE_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
break;
case provider_type::s3:
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_S3_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_S3_CONFIG][JSON_SECRET_KEY] = "";
break;
case provider_type::sia:
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
data[JSON_HOST_CONFIG][JSON_API_PASSWORD] = "";
break;
default:
return;
}
}
auto clean_json_value(std::string_view name, std::string_view data)
-> std::string {
if (name ==
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD) ||
name == fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN) ||
name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY) ||
name == JSON_API_PASSWORD) {
return "";
}
return std::string{data};
}
auto database_type_from_string(std::string type, database_type default_type) auto database_type_from_string(std::string type, database_type default_type)
-> database_type { -> database_type {
type = utils::string::to_lower(utils::string::trim(type)); type = utils::string::to_lower(utils::string::trim(type));
@ -191,4 +237,34 @@ auto api_error_to_string(const api_error &error) -> const std::string & {
return LOOKUP.at(error); return LOOKUP.at(error);
} }
auto provider_type_from_string(std::string_view type,
provider_type default_type) -> provider_type {
auto type_lower = utils::string::to_lower(std::string{type});
if (type_lower == "encrypt") {
return provider_type::encrypt;
}
if (type_lower == "remote") {
return provider_type::remote;
}
if (type_lower == "s3") {
return provider_type::s3;
}
if (type_lower == "sia") {
return provider_type::sia;
}
if (type_lower == "unknown") {
return provider_type::unknown;
}
return default_type;
}
auto provider_type_to_string(provider_type type) -> std::string {
return app_config::get_provider_name(type);
}
} // namespace repertory } // namespace repertory

View File

@ -45,7 +45,7 @@ void get_api_authentication_data(std::string &user, std::string &password,
if (success) { if (success) {
if (user.empty() && password.empty()) { if (user.empty() && password.empty()) {
password = data[JSON_API_AUTH].get<std::string>(); password = data[JSON_API_PASSWORD].get<std::string>();
user = data[JSON_API_USER].get<std::string>(); user = data[JSON_API_USER].get<std::string>();
} }
port = data[JSON_API_PORT].get<std::uint16_t>(); port = data[JSON_API_PORT].get<std::uint16_t>();

View File

@ -44,11 +44,13 @@ struct repertory_exception_handler final
} }
}; };
std::unique_ptr<repertory_exception_handler> handler{([]() -> auto * { const auto repertory_handler{
auto *ptr = new repertory_exception_handler{}; ([]() -> auto {
repertory::utils::error::set_exception_handler(ptr); auto ptr = std::make_unique<repertory_exception_handler>();
repertory::utils::error::set_exception_handler(ptr.get());
return ptr; return ptr;
})()}; })(),
};
} // namespace } // namespace
namespace repertory::utils::error { namespace repertory::utils::error {

View File

@ -36,6 +36,7 @@
#include "cli/pinned_status.hpp" #include "cli/pinned_status.hpp"
#include "cli/set.hpp" #include "cli/set.hpp"
#include "cli/status.hpp" #include "cli/status.hpp"
#include "cli/ui.hpp"
#include "cli/unmount.hpp" #include "cli/unmount.hpp"
#include "cli/unpin_file.hpp" #include "cli/unpin_file.hpp"
#include "utils/cli_utils.hpp" #include "utils/cli_utils.hpp"
@ -70,6 +71,7 @@ static const std::unordered_map<utils::cli::option, action, option_hasher>
cli::actions::pinned_status}, cli::actions::pinned_status},
{utils::cli::options::set_option, cli::actions::set}, {utils::cli::options::set_option, cli::actions::set},
{utils::cli::options::status_option, cli::actions::status}, {utils::cli::options::status_option, cli::actions::status},
{utils::cli::options::ui_option, cli::actions::ui},
{utils::cli::options::unmount_option, cli::actions::unmount}, {utils::cli::options::unmount_option, cli::actions::unmount},
{utils::cli::options::unpin_file_option, cli::actions::unpin_file}, {utils::cli::options::unpin_file_option, cli::actions::unpin_file},
}; };

View File

@ -36,9 +36,8 @@ template <typename drive> inline void help(std::vector<const char *> args) {
std::cout << " -di,--drive_information Display mounted drive " std::cout << " -di,--drive_information Display mounted drive "
"information" "information"
<< std::endl; << std::endl;
std::cout std::cout << " -na,--name Unique configuration "
<< " -na,--name Unique name for S3 or Sia " "name [Required for Encrypt, S3 and Sia]"
"instance [Required]"
<< std::endl; << std::endl;
std::cout << " -s3,--s3 Enables S3 mode" std::cout << " -s3,--s3 Enables S3 mode"
<< std::endl; << std::endl;
@ -79,6 +78,12 @@ template <typename drive> inline void help(std::vector<const char *> args) {
<< std::endl; << std::endl;
std::cout << " -status Display mount status" std::cout << " -status Display mount status"
<< std::endl; << std::endl;
std::cout
<< " -ui,--ui Run embedded management UI"
<< std::endl;
std::cout << " -up,--ui_port Custom port for embedded "
"management UI"
<< std::endl;
std::cout << " -unmount,--unmount Unmount and shutdown" std::cout << " -unmount,--unmount Unmount and shutdown"
<< std::endl; << std::endl;
std::cout << " -uf,--unpin_file [API path] Unpin a file from cache " std::cout << " -uf,--unpin_file [API path] Unpin a file from cache "

View File

@ -29,8 +29,7 @@
#include "types/repertory.hpp" #include "types/repertory.hpp"
#include "utils/cli_utils.hpp" #include "utils/cli_utils.hpp"
#include "utils/com_init_wrapper.hpp" #include "utils/com_init_wrapper.hpp"
#include "utils/file_utils.hpp" #include "utils/file.hpp"
#include "utils/string.hpp"
#if defined(_WIN32) #if defined(_WIN32)
#include "drives/winfsp/remotewinfsp/remote_client.hpp" #include "drives/winfsp/remotewinfsp/remote_client.hpp"
@ -57,24 +56,27 @@ namespace repertory::cli::actions {
mount(std::vector<const char *> args, std::string data_directory, mount(std::vector<const char *> args, std::string data_directory,
int &mount_result, provider_type prov, const std::string &remote_host, int &mount_result, provider_type prov, const std::string &remote_host,
std::uint16_t remote_port, const std::string &unique_id) -> exit_code { std::uint16_t remote_port, const std::string &unique_id) -> exit_code {
auto ret = exit_code::success;
lock_data lock(prov, unique_id); lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(); auto lock_result = lock.grab_lock();
if (res == lock_result::locked) { if (lock_result == lock_result::locked) {
ret = exit_code::mount_active;
std::cerr << app_config::get_provider_display_name(prov) std::cerr << app_config::get_provider_display_name(prov)
<< " mount is already active" << std::endl; << " mount is already active" << std::endl;
} else if (res == lock_result::success) { return exit_code::mount_active;
const auto generate_config = utils::cli::has_option( }
args, utils::cli::options::generate_config_option);
if (generate_config) { if (lock_result != lock_result::success) {
std::cerr << "FATAL: Unable to get provider lock" << std::endl;
return exit_code::lock_failed;
}
if (utils::cli::has_option(args,
utils::cli::options::generate_config_option)) {
app_config config(prov, data_directory); app_config config(prov, data_directory);
if (prov == provider_type::remote) { if (prov == provider_type::remote) {
auto cfg = config.get_remote_config(); auto remote_config = config.get_remote_config();
cfg.host_name_or_ip = remote_host; remote_config.host_name_or_ip = remote_host;
cfg.api_port = remote_port; remote_config.api_port = remote_port;
config.set_remote_config(cfg); config.set_remote_config(remote_config);
} else if (prov == provider_type::sia && } else if (prov == provider_type::sia &&
config.get_sia_config().bucket.empty()) { config.get_sia_config().bucket.empty()) {
[[maybe_unused]] auto bucket = [[maybe_unused]] auto bucket =
@ -84,103 +86,111 @@ mount(std::vector<const char *> args, std::string data_directory,
std::cout << "Generated " << app_config::get_provider_display_name(prov) std::cout << "Generated " << app_config::get_provider_display_name(prov)
<< " Configuration" << std::endl; << " Configuration" << std::endl;
std::cout << config.get_config_file_path() << std::endl; std::cout << config.get_config_file_path() << std::endl;
ret = utils::file::file(config.get_config_file_path()).exists() return utils::file::file(config.get_config_file_path()).exists()
? exit_code::success ? exit_code::success
: exit_code::file_creation_failed; : exit_code::file_creation_failed;
} else { }
#if defined(_WIN32) #if defined(_WIN32)
if (utils::cli::has_option(args, utils::cli::options::hidden_option)) { if (utils::cli::has_option(args, utils::cli::options::hidden_option)) {
::ShowWindow(::GetConsoleWindow(), SW_HIDE); ::ShowWindow(::GetConsoleWindow(), SW_HIDE);
} }
#endif // defined(_WIN32) #endif // defined(_WIN32)
auto drive_args =
utils::cli::parse_drive_options(args, prov, data_directory); lock_data global_lock(provider_type::unknown, "global");
lock_result = global_lock.grab_lock(100U);
if (lock_result != lock_result::success) {
std::cerr << "FATAL: Unable to get global lock" << std::endl;
return exit_code::lock_failed;
}
auto drive_args = utils::cli::parse_drive_options(args, prov, data_directory);
app_config config(prov, data_directory); app_config config(prov, data_directory);
{
std::uint16_t port{};
if (not utils::get_next_available_port(config.get_api_port(), port)) {
std::cerr << "FATAL: Unable to get available port" << std::endl;
return exit_code::startup_exception;
}
config.set_api_port(port);
}
#if defined(_WIN32) #if defined(_WIN32)
if (config.get_enable_mount_manager() && if (config.get_enable_mount_manager() && not utils::is_process_elevated()) {
not utils::is_process_elevated()) { utils::com_init_wrapper wrapper;
utils::com_init_wrapper cw;
if (not lock.set_mount_state(true, "elevating", -1)) { if (not lock.set_mount_state(true, "elevating", -1)) {
std::cerr << "failed to set mount state" << std::endl; std::cerr << "failed to set mount state" << std::endl;
} }
lock.release(); lock.release();
global_lock.release();
mount_result = utils::run_process_elevated(args); mount_result = utils::run_process_elevated(args);
lock_data lock2(prov, unique_id); lock_data prov_lock(prov, unique_id);
if (lock2.grab_lock() == lock_result::success) { if (prov_lock.grab_lock() == lock_result::success) {
if (not lock2.set_mount_state(false, "", -1)) { if (not prov_lock.set_mount_state(false, "", -1)) {
std::cerr << "failed to set mount state" << std::endl; std::cerr << "failed to set mount state" << std::endl;
} }
lock2.release(); prov_lock.release();
} }
return exit_code::mount_result; return exit_code::mount_result;
} }
#endif // defined(_WIN32) #endif // defined(_WIN32)
std::cout << "Initializing "
<< app_config::get_provider_display_name(prov) std::cout << "Initializing " << app_config::get_provider_display_name(prov)
<< (unique_id.empty() ? "" << (unique_id.empty() ? ""
: (prov == provider_type::s3 || prov == provider_type::sia) : (prov == provider_type::remote)
? " [" + unique_id + ']' ? " [" + remote_host + ':' + std::to_string(remote_port) +
: " [" + remote_host + ':' + ']'
std::to_string(remote_port) + ']') : " [" + unique_id + ']')
<< " Drive" << std::endl; << " Drive" << std::endl;
if (prov == provider_type::remote) { if (prov == provider_type::remote) {
std::uint16_t port{0U};
if (utils::get_next_available_port(config.get_api_port(), port)) {
auto cfg = config.get_remote_config();
cfg.host_name_or_ip = remote_host;
cfg.api_port = remote_port;
config.set_remote_config(cfg);
config.set_api_port(port);
try { try {
auto remote_cfg = config.get_remote_config();
remote_cfg.host_name_or_ip = remote_host;
remote_cfg.api_port = remote_port;
config.set_remote_config(remote_cfg);
remote_drive drive( remote_drive drive(
config, config,
[&config]() -> std::unique_ptr<remote_instance> { [&config]() -> std::unique_ptr<remote_instance> {
return std::unique_ptr<remote_instance>( return std::unique_ptr<remote_instance>(new remote_client(config));
new remote_client(config));
}, },
lock); lock);
if (not lock.set_mount_state(true, "", -1)) { if (not lock.set_mount_state(true, "", -1)) {
std::cerr << "failed to set mount state" << std::endl; std::cerr << "failed to set mount state" << std::endl;
} }
global_lock.release();
mount_result = drive.mount(drive_args); mount_result = drive.mount(drive_args);
ret = exit_code::mount_result; return exit_code::mount_result;
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << "FATAL: " << e.what() << std::endl; std::cerr << "FATAL: " << e.what() << std::endl;
ret = exit_code::startup_exception;
} }
} else {
std::cerr << "FATAL: Unable to get available port" << std::endl; return exit_code::startup_exception;
ret = exit_code::startup_exception;
} }
} else {
if (prov == provider_type::sia && try {
config.get_sia_config().bucket.empty()) { if (prov == provider_type::sia && config.get_sia_config().bucket.empty()) {
[[maybe_unused]] auto bucket = [[maybe_unused]] auto bucket =
config.set_value_by_name("SiaConfig.Bucket", unique_id); config.set_value_by_name("SiaConfig.Bucket", unique_id);
} }
try {
auto provider = create_provider(prov, config); auto provider = create_provider(prov, config);
repertory_drive drive(config, lock, *provider); repertory_drive drive(config, lock, *provider);
if (not lock.set_mount_state(true, "", -1)) { if (not lock.set_mount_state(true, "", -1)) {
std::cerr << "failed to set mount state" << std::endl; std::cerr << "failed to set mount state" << std::endl;
} }
global_lock.release();
mount_result = drive.mount(drive_args); mount_result = drive.mount(drive_args);
ret = exit_code::mount_result; return exit_code::mount_result;
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << "FATAL: " << e.what() << std::endl; std::cerr << "FATAL: " << e.what() << std::endl;
ret = exit_code::startup_exception;
}
}
}
} else {
ret = exit_code::lock_failed;
} }
return ret; return exit_code::startup_exception;
} }
} // namespace repertory::cli::actions } // namespace repertory::cli::actions

View File

@ -0,0 +1,61 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#ifndef REPERTORY_INCLUDE_CLI_UI_HPP_
#define REPERTORY_INCLUDE_CLI_UI_HPP_
#include "types/repertory.hpp"
#include "ui/handlers.hpp"
#include "ui/mgmt_app_config.hpp"
#include "utils/cli_utils.hpp"
#include "utils/file.hpp"
#include "utils/string.hpp"
namespace repertory::cli::actions {
[[nodiscard]] inline auto
ui(std::vector<const char *> args, const std::string & /*data_directory*/,
const provider_type & /* prov */, const std::string & /* unique_id */,
std::string /* user */, std::string /* password */) -> exit_code {
ui::mgmt_app_config config{};
std::string data;
auto res = utils::cli::parse_string_option(
args, utils::cli::options::ui_port_option, data);
if (res == exit_code::success && not data.empty()) {
config.set_api_port(utils::string::to_uint16(data));
}
if (not utils::file::change_to_process_directory()) {
return exit_code::ui_mount_failed;
}
httplib::Server server;
if (not server.set_mount_point("/ui", "./web")) {
return exit_code::ui_mount_failed;
}
ui::handlers handlers(&config, &server);
return exit_code::success;
}
} // namespace repertory::cli::actions
#endif // REPERTORY_INCLUDE_CLI_UI_HPP_

View File

@ -0,0 +1,117 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#ifndef REPERTORY_INCLUDE_UI_HANDLERS_HPP_
#define REPERTORY_INCLUDE_UI_HANDLERS_HPP_
#include "events/consumers/console_consumer.hpp"
#include "utils/common.hpp"
namespace repertory::ui {
class mgmt_app_config;
class handlers final {
private:
static constexpr const auto nonce_length{128U};
static constexpr const auto nonce_timeout{15U};
struct nonce_data final {
std::chrono::system_clock::time_point creation{
std::chrono::system_clock::now(),
};
std::string nonce{
utils::generate_random_string(nonce_length),
};
};
public:
handlers(mgmt_app_config *config, httplib::Server *server);
handlers() = delete;
handlers(const handlers &) = delete;
handlers(handlers &&) = delete;
~handlers();
auto operator=(const handlers &) -> handlers & = delete;
auto operator=(handlers &&) -> handlers & = delete;
private:
mgmt_app_config *config_;
std::string repertory_binary_;
httplib::Server *server_;
private:
console_consumer console;
mutable std::mutex mtx_;
mutable std::unordered_map<std::string, std::recursive_mutex> mtx_lookup_;
std::mutex nonce_mtx_;
std::unordered_map<std::string, nonce_data> nonce_lookup_;
std::condition_variable nonce_notify_;
std::unique_ptr<std::thread> nonce_thread_;
stop_type stop_requested{false};
private:
[[nodiscard]] auto data_directory_exists(provider_type prov,
std::string_view name) const -> bool;
void handle_get_available_locations(httplib::Response &res) const;
void handle_get_mount(const httplib::Request &req,
httplib::Response &res) const;
void handle_get_mount_list(httplib::Response &res) const;
void handle_get_mount_location(const httplib::Request &req,
httplib::Response &res) const;
void handle_get_mount_status(const httplib::Request &req,
httplib::Response &res) const;
void handle_get_nonce(httplib::Response &res);
void handle_get_settings(httplib::Response &res) const;
void handle_post_add_mount(const httplib::Request &req,
httplib::Response &res) const;
void handle_post_mount(const httplib::Request &req, httplib::Response &res);
void handle_put_set_value_by_name(const httplib::Request &req,
httplib::Response &res) const;
void handle_put_settings(const httplib::Request &req,
httplib::Response &res) const;
auto launch_process(provider_type prov, std::string_view name,
std::vector<std::string> args,
bool background = false) const
-> std::vector<std::string>;
void removed_expired_nonces();
void set_key_value(provider_type prov, std::string_view name,
std::string_view key, std::string_view value) const;
};
} // namespace repertory::ui
#endif // REPERTORY_INCLUDE_UI_HANDLERS_HPP_

View File

@ -0,0 +1,70 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#ifndef REPERTORY_INCLUDE_UI_MGMT_APP_CONFIG_HPP_
#define REPERTORY_INCLUDE_UI_MGMT_APP_CONFIG_HPP_
#include "types/repertory.hpp"
namespace repertory::ui {
class mgmt_app_config final {
public:
mgmt_app_config();
private:
atomic<std::string> api_password_{"repertory"};
std::atomic<std::uint16_t> api_port_{default_ui_mgmt_port};
atomic<std::string> api_user_{"repertory"};
std::unordered_map<provider_type,
std::unordered_map<std::string, std::string>>
locations_;
mutable std::recursive_mutex mtx_;
private:
void save() const;
public:
[[nodiscard]] auto to_json() const -> nlohmann::json;
[[nodiscard]] auto get_api_password() const -> std::string {
return api_password_;
}
[[nodiscard]] auto get_api_port() const -> std::uint16_t { return api_port_; }
[[nodiscard]] auto get_api_user() const -> std::string { return api_user_; }
[[nodiscard]] auto get_mount_location(provider_type prov,
std::string_view name) const
-> std::string;
void set_api_password(std::string_view api_password);
void set_api_port(std::uint16_t api_port);
void set_api_user(std::string_view api_user);
void set_mount_location(provider_type prov, std::string_view name,
std::string_view location);
};
} // namespace repertory::ui
#endif // REPERTORY_INCLUDE_UI_MGMT_APP_CONFIG_HPP_

View File

@ -105,7 +105,8 @@ auto main(int argc, char **argv) -> int {
} }
} }
} }
} else if ((prov == provider_type::s3) || (prov == provider_type::sia)) { } else if ((prov == provider_type::s3) || (prov == provider_type::sia) ||
(prov == provider_type::encrypt)) {
std::string data; std::string data;
res = utils::cli::parse_string_option( res = utils::cli::parse_string_option(
args, utils::cli::options::name_option, data); args, utils::cli::options::name_option, data);
@ -115,9 +116,9 @@ auto main(int argc, char **argv) -> int {
if (prov == provider_type::sia) { if (prov == provider_type::sia) {
unique_id = "default"; unique_id = "default";
} else { } else {
std::cerr << "Name of " std::cerr << "Configuration name for '"
<< app_config::get_provider_display_name(prov) << app_config::get_provider_display_name(prov)
<< " instance not provided" << std::endl; << "' was not provided" << std::endl;
res = exit_code::invalid_syntax; res = exit_code::invalid_syntax;
} }
} }
@ -146,10 +147,17 @@ auto main(int argc, char **argv) -> int {
(res == exit_code::option_not_found) && (res == exit_code::option_not_found) &&
(idx < utils::cli::options::option_list.size()); (idx < utils::cli::options::option_list.size());
idx++) { idx++) {
try {
res = cli::actions::perform_action( res = cli::actions::perform_action(
utils::cli::options::option_list[idx], args, data_directory, prov, utils::cli::options::option_list[idx], args, data_directory, prov,
unique_id, user, password); unique_id, user, password);
} catch (const std::exception &ex) {
res = exit_code::exception;
} catch (...) {
res = exit_code::exception;
} }
}
if (res == exit_code::option_not_found) { if (res == exit_code::option_not_found) {
res = cli::actions::mount(args, data_directory, mount_result, prov, res = cli::actions::mount(args, data_directory, mount_result, prov,
remote_host, remote_port, unique_id); remote_host, remote_port, unique_id);

View File

@ -0,0 +1,762 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#include "ui/handlers.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp"
#include "types/repertory.hpp"
#include "ui/mgmt_app_config.hpp"
#include "utils/collection.hpp"
#include "utils/common.hpp"
#include "utils/config.hpp"
#include "utils/error_utils.hpp"
#include "utils/file.hpp"
#include "utils/hash.hpp"
#include "utils/path.hpp"
#include "utils/string.hpp"
#include <boost/process.hpp>
namespace {
[[nodiscard]] auto decrypt(std::string_view data, std::string_view password)
-> std::string {
REPERTORY_USES_FUNCTION_NAME();
if (data.empty()) {
return std::string{data};
}
repertory::data_buffer decoded;
if (not repertory::utils::collection::from_hex_string(data, decoded)) {
throw repertory::utils::error::create_exception(function_name,
{"decryption failed"});
}
repertory::data_buffer buffer(decoded.size());
auto key = repertory::utils::encryption::create_hash_blake2b_256(password);
unsigned long long size{};
auto res = crypto_aead_xchacha20poly1305_ietf_decrypt(
reinterpret_cast<unsigned char *>(buffer.data()), &size, nullptr,
reinterpret_cast<const unsigned char *>(
&decoded.at(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES)),
decoded.size() - crypto_aead_xchacha20poly1305_IETF_NPUBBYTES,
reinterpret_cast<const unsigned char *>(REPERTORY.data()),
REPERTORY.length(),
reinterpret_cast<const unsigned char *>(decoded.data()),
reinterpret_cast<const unsigned char *>(key.data()));
if (res != 0) {
throw repertory::utils::error::create_exception(function_name,
{"decryption failed"});
}
return {
buffer.begin(),
std::next(buffer.begin(), static_cast<std::int64_t>(size)),
};
}
[[nodiscard]] auto decrypt_value(const repertory::ui::mgmt_app_config *config,
std::string_view key, std::string_view value,
bool &skip) -> std::string {
auto last_key{key};
auto parts = repertory::utils::string::split(key, '.', false);
if (parts.size() > 1U) {
last_key = parts.at(parts.size() - 1U);
}
if (last_key == repertory::JSON_API_PASSWORD ||
last_key == repertory::JSON_ENCRYPTION_TOKEN ||
last_key == repertory::JSON_SECRET_KEY) {
auto decrypted = decrypt(value, config->get_api_password());
if (decrypted.empty()) {
skip = true;
}
return decrypted;
}
return std::string{value};
}
} // namespace
namespace repertory::ui {
handlers::handlers(mgmt_app_config *config, httplib::Server *server)
: config_(config),
#if defined(_WIN32)
repertory_binary_(utils::path::combine(".", {"repertory.exe"})),
#else // !defined(_WIN32)
repertory_binary_(utils::path::combine(".", {"repertory"})),
#endif // defined(_WIN32)
server_(server) {
REPERTORY_USES_FUNCTION_NAME();
#ifdef _WIN32
server_->set_socket_options([](auto &&sock) {
int enable = 1;
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *>(&enable), sizeof(enable));
});
#endif // _WIN32
server_->set_pre_routing_handler(
[this](const httplib::Request &req,
auto &&res) -> httplib::Server::HandlerResponse {
if (req.path == "/api/v1/nonce" || req.path == "/ui" ||
req.path.starts_with("/ui/")) {
return httplib::Server::HandlerResponse::Unhandled;
}
auto auth =
decrypt(req.get_param_value("auth"), config_->get_api_password());
if (utils::string::begins_with(
auth, fmt::format("{}_", config_->get_api_user()))) {
auto nonce = auth.substr(config_->get_api_user().length() + 1U);
mutex_lock lock(nonce_mtx_);
if (nonce_lookup_.contains(nonce)) {
nonce_lookup_.erase(nonce);
return httplib::Server::HandlerResponse::Unhandled;
}
}
res.status = http_error_codes::unauthorized;
return httplib::Server::HandlerResponse::Handled;
});
server_->set_exception_handler([](const httplib::Request &req,
httplib::Response &res,
std::exception_ptr ptr) {
json data{
{"path", req.path},
};
try {
std::rethrow_exception(ptr);
} catch (const std::exception &e) {
data["error"] = (e.what() == nullptr) ? "unknown error" : e.what();
utils::error::raise_error(function_name, e,
"failed request: " + req.path);
} catch (...) {
data["error"] = "unknown error";
utils::error::raise_error(function_name, "unknown error",
"failed request: " + req.path);
}
res.set_content(data.dump(), "application/json");
res.status = utils::string::ends_with(data["error"].get<std::string>(),
"|decryption failed")
? http_error_codes::unauthorized
: http_error_codes::internal_error;
});
server->Get("/api/v1/locations", [this](auto && /* req */, auto &&res) {
handle_get_available_locations(res);
});
server->Get("/api/v1/mount",
[this](auto &&req, auto &&res) { handle_get_mount(req, res); });
server->Get("/api/v1/mount_location", [this](auto &&req, auto &&res) {
handle_get_mount_location(req, res);
});
server->Get("/api/v1/mount_list", [this](auto && /* req */, auto &&res) {
handle_get_mount_list(res);
});
server->Get("/api/v1/mount_status", [this](auto &&req, auto &&res) {
handle_get_mount_status(req, res);
});
server->Get("/api/v1/nonce",
[this](auto && /* req */, auto &&res) { handle_get_nonce(res); });
server->Get("/api/v1/settings", [this](auto && /* req */, auto &&res) {
handle_get_settings(res);
});
server->Post("/api/v1/add_mount", [this](auto &&req, auto &&res) {
handle_post_add_mount(req, res);
});
server->Post("/api/v1/mount",
[this](auto &&req, auto &&res) { handle_post_mount(req, res); });
server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
handle_put_set_value_by_name(req, res);
});
server->Put("/api/v1/settings", [this](auto &&req, auto &&res) {
handle_put_settings(req, res);
});
static std::atomic<httplib::Server *> this_server{server_};
static const auto quit_handler = [](int /* sig */) {
auto *ptr = this_server.load();
if (ptr == nullptr) {
return;
}
this_server = nullptr;
ptr->stop();
};
std::signal(SIGINT, quit_handler);
#if !defined(_WIN32)
std::signal(SIGQUIT, quit_handler);
#endif // !defined(_WIN32)
std::signal(SIGTERM, quit_handler);
#if defined(_WIN32)
system(fmt::format(
R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#elif defined(__linux__)
system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#else // error
build fails here
#endif
std::uint16_t port{};
if (not utils::get_next_available_port(config_->get_api_port(), port)) {
fmt::println("failed to detect if port is available|{}",
config_->get_api_port());
return;
}
if (port != config_->get_api_port()) {
fmt::println("failed to listen on port|{}|next available|{}",
config_->get_api_port(), port);
return;
}
event_system::instance().start();
nonce_thread_ =
std::make_unique<std::thread>([this]() { removed_expired_nonces(); });
server_->listen("127.0.0.1", config_->get_api_port());
if (this_server != nullptr) {
this_server = nullptr;
server_->stop();
}
}
handlers::~handlers() {
if (nonce_thread_) {
stop_requested = true;
unique_mutex_lock lock(nonce_mtx_);
nonce_notify_.notify_all();
lock.unlock();
nonce_thread_->join();
nonce_thread_.reset();
}
event_system::instance().stop();
}
auto handlers::data_directory_exists(provider_type prov,
std::string_view name) const -> bool {
auto data_dir = utils::path::combine(app_config::get_root_data_directory(),
{
app_config::get_provider_name(prov),
name,
});
auto ret = utils::file::directory{data_dir}.exists();
if (ret) {
return ret;
}
unique_mutex_lock lock(mtx_);
mtx_lookup_.erase(
fmt::format("{}-{}", name, app_config::get_provider_name(prov)));
lock.unlock();
return ret;
}
void handlers::handle_get_available_locations(httplib::Response &res) const {
#if defined(_WIN32)
constexpr const std::array<std::string_view, 26U> letters{
"A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:",
"J:", "K:", "L:", "M:", "N:", "O:", "P:", "Q:", "R:",
"S:", "T:", "U:", "V:", "W:", "X:", "Y:", "Z:",
};
auto available = std::accumulate(
letters.begin(), letters.end(), std::vector<std::string_view>(),
[](auto &&vec, auto &&letter) -> std::vector<std::string_view> {
if (utils::file::directory{utils::path::combine(letter, {"\\"})}
.exists()) {
return vec;
}
vec.emplace_back(letter);
return vec;
});
res.set_content(nlohmann::json(available).dump(), "application/json");
#else // !defined(_WIN32)
res.set_content(nlohmann::json(std::vector<std::string_view>()).dump(),
"application/json");
#endif // defined(_WIN32)
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount(const httplib::Request &req,
httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
auto prov = provider_type_from_string(req.get_param_value("type"));
auto name = req.get_param_value("name");
if (not data_directory_exists(prov, name)) {
res.status = http_error_codes::not_found;
return;
}
auto lines = launch_process(prov, name, {"-dc"});
if (lines.at(0U) != "0") {
throw utils::error::create_exception(function_name, {
"command failed",
lines.at(0U),
});
}
lines.erase(lines.begin());
auto result = nlohmann::json::parse(utils::string::join(lines, '\n'));
clean_json_config(prov, result);
res.set_content(result.dump(), "application/json");
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount_list(httplib::Response &res) const {
auto data_dir = utils::file::directory{app_config::get_root_data_directory()};
nlohmann::json result;
const auto process_dir = [&data_dir, &result](std::string_view name) {
auto name_dir = data_dir.get_directory(name);
if (not name_dir) {
return;
}
for (const auto &dir : name_dir->get_directories()) {
if (not dir->get_file("config.json")) {
continue;
}
result[name].emplace_back(
utils::path::strip_to_file_name(dir->get_path()));
}
};
process_dir("encrypt");
process_dir("remote");
process_dir("s3");
process_dir("sia");
res.set_content(result.dump(), "application/json");
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount_location(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
if (not data_directory_exists(prov, name)) {
res.status = http_error_codes::not_found;
return;
}
res.set_content(
nlohmann::json({
{"Location", config_->get_mount_location(prov, name)},
})
.dump(),
"application/json");
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount_status(const httplib::Request &req,
httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
if (not data_directory_exists(prov, name)) {
res.status = http_error_codes::not_found;
return;
}
auto status_name = app_config::get_provider_display_name(prov);
switch (prov) {
case provider_type::remote: {
auto parts = utils::string::split(name, '_', false);
status_name =
fmt::format("{}{}:{}", status_name, parts.at(0U), parts.at(1U));
} break;
case provider_type::encrypt:
case provider_type::sia:
case provider_type::s3:
status_name = fmt::format("{}{}", status_name, name);
break;
default:
throw utils::error::create_exception(function_name,
{
"provider is not supported",
provider_type_to_string(prov),
name,
});
}
auto lines = launch_process(prov, name, {"-status"});
nlohmann::json result(
nlohmann::json::parse(utils::string::join(lines, '\n')).at(status_name));
if (result.at("Location").get<std::string>().empty()) {
result.at("Location") = config_->get_mount_location(prov, name);
} else if (result.at("Active").get<bool>()) {
config_->set_mount_location(prov, name,
result.at("Location").get<std::string>());
}
res.set_content(result.dump(), "application/json");
res.status = http_error_codes::ok;
}
void handlers::handle_get_nonce(httplib::Response &res) {
mutex_lock lock(nonce_mtx_);
nonce_data nonce{};
nonce_lookup_[nonce.nonce] = nonce;
nlohmann::json data({{"nonce", nonce.nonce}});
res.set_content(data.dump(), "application/json");
res.status = http_error_codes::ok;
}
void handlers::handle_get_settings(httplib::Response &res) const {
auto settings = config_->to_json();
settings[JSON_API_PASSWORD] = "";
settings.erase(JSON_MOUNT_LOCATIONS);
res.set_content(settings.dump(), "application/json");
res.status = http_error_codes::ok;
}
void handlers::handle_post_add_mount(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
if (data_directory_exists(prov, name)) {
res.status = http_error_codes::ok;
return;
}
auto cfg = nlohmann::json::parse(req.get_param_value("config"));
std::map<std::string, std::string> values{};
for (const auto &[key, value] : cfg.items()) {
if (value.is_object()) {
for (const auto &[key2, value2] : value.items()) {
auto subKey = fmt::format("{}.{}", key, key2);
auto skip{false};
auto decrypted = decrypt_value(
config_, subKey, value2.template get<std::string>(), skip);
if (skip) {
continue;
}
values[subKey] = decrypted;
}
continue;
}
auto skip{false};
auto decrypted =
decrypt_value(config_, key, value.template get<std::string>(), skip);
if (skip) {
continue;
}
values[key] = decrypted;
}
launch_process(prov, name, {"-gc"});
for (auto &[key, value] : values) {
set_key_value(prov, name, key, value);
}
res.status = http_error_codes::ok;
}
void handlers::handle_post_mount(const httplib::Request &req,
httplib::Response &res) {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
if (not data_directory_exists(prov, name)) {
res.status = http_error_codes::not_found;
return;
}
auto location = utils::path::absolute(req.get_param_value("location"));
auto unmount = utils::string::to_bool(req.get_param_value("unmount"));
if (unmount) {
launch_process(prov, name, {"-unmount"});
} else {
#if defined(_WIN32)
if (utils::file::directory{location}.exists()) {
#else // !defined(_WIN32)
if (not utils::file::directory{location}.exists()) {
#endif // defined(_WIN32)
config_->set_mount_location(prov, name, "");
res.status = http_error_codes::internal_error;
return;
}
static std::mutex mount_mtx;
mutex_lock lock(mount_mtx);
launch_process(prov, name, {location}, true);
config_->set_mount_location(prov, name, location);
launch_process(prov, name, {"-status"});
}
res.status = http_error_codes::ok;
}
void handlers::handle_put_set_value_by_name(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
if (not data_directory_exists(prov, name)) {
res.status = http_error_codes::not_found;
return;
}
auto key = req.get_param_value("key");
auto value = req.get_param_value("value");
auto skip{false};
value = decrypt_value(config_, key, value, skip);
if (not skip) {
set_key_value(prov, name, key, value);
}
res.status = http_error_codes::ok;
}
void handlers::handle_put_settings(const httplib::Request &req,
httplib::Response &res) const {
nlohmann::json data = nlohmann::json::parse(req.get_param_value("data"));
if (data.contains(JSON_API_PASSWORD)) {
auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),
config_->get_api_password());
if (not password.empty()) {
config_->set_api_password(password);
}
}
if (data.contains(JSON_API_PORT)) {
config_->set_api_port(
utils::string::to_uint16(data.at(JSON_API_PORT).get<std::string>()));
}
if (data.contains(JSON_API_USER)) {
config_->set_api_user(data.at(JSON_API_USER).get<std::string>());
}
res.status = http_error_codes::ok;
}
auto handlers::launch_process(provider_type prov, std::string_view name,
std::vector<std::string> args,
bool background) const
-> std::vector<std::string> {
REPERTORY_USES_FUNCTION_NAME();
switch (prov) {
case provider_type::encrypt:
args.insert(args.begin(), "-en");
args.insert(std::next(args.begin()), "-na");
args.insert(std::next(args.begin(), 2U), std::string{name});
break;
case provider_type::remote: {
auto parts = utils::string::split(name, '_', false);
args.insert(args.begin(), "-rm");
args.insert(std::next(args.begin()),
fmt::format("{}:{}", parts.at(0U), parts.at(1U)));
} break;
case provider_type::s3:
args.insert(args.begin(), "-s3");
args.insert(std::next(args.begin()), "-na");
args.insert(std::next(args.begin(), 2U), std::string{name});
break;
case provider_type::sia:
args.insert(args.begin(), "-na");
args.insert(std::next(args.begin()), std::string{name});
break;
default:
throw utils::error::create_exception(function_name,
{
"provider is not supported",
provider_type_to_string(prov),
name,
});
}
unique_mutex_lock lock(mtx_);
auto &inst_mtx = mtx_lookup_[fmt::format(
"{}-{}", name, app_config::get_provider_name(prov))];
lock.unlock();
recur_mutex_lock inst_lock(inst_mtx);
if (background) {
#if defined(_WIN32)
std::array<char, MAX_PATH + 1U> path{};
::GetSystemDirectoryA(path.data(), path.size());
args.insert(args.begin(), utils::path::combine(path.data(), {"cmd.exe"}));
args.insert(std::next(args.begin()), "/c");
args.insert(std::next(args.begin(), 2U), "start");
args.insert(std::next(args.begin(), 3U), "");
args.insert(std::next(args.begin(), 4U), "/MIN");
args.insert(std::next(args.begin(), 5U), repertory_binary_);
#elif defined(__linux__) // defined(__linux__)
args.insert(args.begin(), repertory_binary_);
#else // !defined(__linux__) && !defined(_WIN32)
build fails here
#endif // defined(_WIN32)
std::vector<const char *> exec_args;
exec_args.reserve(args.size() + 1U);
for (const auto &arg : args) {
exec_args.push_back(arg.c_str());
}
exec_args.push_back(nullptr);
#if defined(_WIN32)
_spawnv(_P_DETACH, exec_args.at(0U),
const_cast<char *const *>(exec_args.data()));
#elif defined(__linux__) // defined(__linux__)
auto pid = fork();
if (pid < 0) {
throw utils::error::create_exception(function_name, {"mount failed"});
}
if (pid != 0) {
return {};
}
setsid();
chdir("/");
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY);
signal(SIGCHLD, SIG_IGN);
execvp(exec_args.at(0U), const_cast<char *const *>(exec_args.data()));
#else // !defined(__linux__) && !defined(_WIN32)
build fails here
#endif // defined(_WIN32)
return {};
}
boost::process::ipstream out;
boost::process::child proc(repertory_binary_, boost::process::args(args),
boost::process::std_out > out);
std::string data;
std::string line;
while (out && std::getline(out, line)) {
data += line + "\n";
}
return utils::string::split(utils::string::replace(data, "\r", ""), '\n',
false);
}
void handlers::removed_expired_nonces() {
unique_mutex_lock lock(nonce_mtx_);
lock.unlock();
while (not stop_requested) {
lock.lock();
auto nonces = nonce_lookup_;
lock.unlock();
for (const auto &[key, value] : nonces) {
if (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - value.creation)
.count() >= nonce_timeout) {
lock.lock();
nonce_lookup_.erase(key);
lock.unlock();
}
}
if (stop_requested) {
break;
}
lock.lock();
if (stop_requested) {
break;
}
nonce_notify_.wait_for(lock, std::chrono::seconds(1U));
lock.unlock();
}
}
void handlers::set_key_value(provider_type prov, std::string_view name,
std::string_view key,
std::string_view value) const {
std::vector<std::string> args;
args.emplace_back("-set");
args.emplace_back(key);
args.emplace_back(value);
launch_process(prov, name, args, false);
}
} // namespace repertory::ui

View File

@ -0,0 +1,202 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#include "ui/mgmt_app_config.hpp"
#include "app_config.hpp"
#include "utils/error_utils.hpp"
#include "utils/file.hpp"
#include "utils/path.hpp"
#include "utils/unix.hpp"
#include "utils/windows.hpp"
namespace {
[[nodiscard]] auto from_json(const nlohmann::json &json)
-> std::unordered_map<repertory::provider_type,
std::unordered_map<std::string, std::string>> {
std::unordered_map<repertory::provider_type,
std::unordered_map<std::string, std::string>>
map_of_maps{
{repertory::provider_type::encrypt, nlohmann::json::object()},
{repertory::provider_type::remote, nlohmann::json::object()},
{repertory::provider_type::s3, nlohmann::json::object()},
{repertory::provider_type::sia, nlohmann::json::object()},
};
if (json.is_null() || json.empty()) {
return map_of_maps;
}
for (auto &[prov, map] : map_of_maps) {
auto prov_str = repertory::provider_type_to_string(prov);
if (!json.contains(prov_str)) {
continue;
}
for (const auto &[key, value] : json.at(prov_str).items()) {
map[key] = value;
}
}
return map_of_maps;
}
[[nodiscard]] auto map_to_json(const auto &map_of_maps) -> nlohmann::json {
auto json = nlohmann::json::object();
for (const auto &[prov, map] : map_of_maps) {
for (const auto &[key, value] : map) {
json[repertory::provider_type_to_string(prov)][key] = value;
}
}
return json;
}
} // namespace
namespace repertory::ui {
mgmt_app_config::mgmt_app_config() {
REPERTORY_USES_FUNCTION_NAME();
auto config_file =
utils::path::combine(app_config::get_root_data_directory(), {"ui.json"});
try {
if (not utils::file::directory{app_config::get_root_data_directory()}
.create_directory()) {
throw utils::error::create_exception(
function_name, {
"failed to create directory",
app_config::get_root_data_directory(),
});
}
nlohmann::json data;
if (utils::file::read_json_file(config_file, data)) {
api_password_ = data.at(JSON_API_PASSWORD).get<std::string>();
api_port_ = data.at(JSON_API_PORT).get<std::uint16_t>();
api_user_ = data.at(JSON_API_USER).get<std::string>();
locations_ = from_json(data.at(JSON_MOUNT_LOCATIONS));
return;
}
utils::error::raise_error(
function_name, utils::get_last_error_code(),
fmt::format("failed to read file|{}", config_file));
save();
} catch (const std::exception &ex) {
utils::error::raise_error(
function_name, ex, fmt::format("failed to read file|{}", config_file));
}
}
auto mgmt_app_config::get_mount_location(provider_type prov,
std::string_view name) const
-> std::string {
recur_mutex_lock lock(mtx_);
if (locations_.contains(prov) &&
locations_.at(prov).contains(std::string{name})) {
return locations_.at(prov).at(std::string{name});
}
return "";
}
void mgmt_app_config::save() const {
REPERTORY_USES_FUNCTION_NAME();
auto config_file =
utils::path::combine(app_config::get_root_data_directory(), {"ui.json"});
try {
if (not utils::file::directory{app_config::get_root_data_directory()}
.create_directory()) {
utils::error::raise_error(
function_name, fmt::format("failed to create directory|{}",
app_config::get_root_data_directory()));
return;
}
if (utils::file::write_json_file(config_file, to_json())) {
return;
}
utils::error::raise_error(
function_name, utils::get_last_error_code(),
fmt::format("failed to save file|{}", config_file));
} catch (const std::exception &ex) {
utils::error::raise_error(
function_name, ex, fmt::format("failed to save file|{}", config_file));
}
}
void mgmt_app_config::set_api_password(std::string_view api_password) {
if (api_password_ == std::string{api_password}) {
return;
}
api_password_ = std::string{api_password};
save();
}
void mgmt_app_config::set_api_port(std::uint16_t api_port) {
if (api_port_ == api_port) {
return;
}
api_port_ = api_port;
save();
}
void mgmt_app_config::set_api_user(std::string_view api_user) {
if (api_user_ == std::string{api_user}) {
return;
}
api_user_ = std::string{api_user};
save();
}
void mgmt_app_config::set_mount_location(provider_type prov,
std::string_view name,
std::string_view location) {
if (name.empty()) {
return;
}
recur_mutex_lock lock(mtx_);
if (locations_[prov][std::string{name}] == std::string{location}) {
return;
}
locations_[prov][std::string{name}] = std::string{location};
save();
}
auto mgmt_app_config::to_json() const -> nlohmann::json {
nlohmann::json data;
data[JSON_API_PASSWORD] = api_password_;
data[JSON_API_PORT] = api_port_;
data[JSON_API_USER] = api_user_;
data[JSON_MOUNT_LOCATIONS] = map_to_json(locations_);
return data;
}
} // namespace repertory::ui

View File

@ -128,7 +128,7 @@ std::atomic<std::uint64_t> app_config_test::idx{0U};
static void defaults_tests(const json &json_data, provider_type prov) { static void defaults_tests(const json &json_data, provider_type prov) {
json json_defaults = { json json_defaults = {
{JSON_API_PORT, app_config::default_rpc_port(prov)}, {JSON_API_PORT, app_config::default_rpc_port()},
{JSON_API_USER, std::string{REPERTORY}}, {JSON_API_USER, std::string{REPERTORY}},
{JSON_DOWNLOAD_TIMEOUT_SECS, default_download_timeout_secs}, {JSON_DOWNLOAD_TIMEOUT_SECS, default_download_timeout_secs},
{JSON_DATABASE_TYPE, database_type::rocksdb}, {JSON_DATABASE_TYPE, database_type::rocksdb},
@ -185,9 +185,9 @@ static void defaults_tests(const json &json_data, provider_type prov) {
} }
fmt::println("testing default|{}-{}", app_config::get_provider_name(prov), fmt::println("testing default|{}-{}", app_config::get_provider_name(prov),
JSON_API_AUTH); JSON_API_PASSWORD);
ASSERT_EQ(std::size_t(default_api_auth_size), ASSERT_EQ(std::size_t(default_api_password_size),
json_data.at(JSON_API_AUTH).get<std::string>().size()); json_data.at(JSON_API_PASSWORD).get<std::string>().size());
for (const auto &[key, value] : json_defaults.items()) { for (const auto &[key, value] : json_defaults.items()) {
fmt::println("testing default|{}-{}", app_config::get_provider_name(prov), fmt::println("testing default|{}-{}", app_config::get_provider_name(prov),
key); key);
@ -216,11 +216,11 @@ static void common_tests(app_config &config, provider_type prov) {
ASSERT_EQ(config.get_provider_type(), prov); ASSERT_EQ(config.get_provider_type(), prov);
std::map<std::string_view, std::function<void(app_config &)>> methods{ std::map<std::string_view, std::function<void(app_config &)>> methods{
{JSON_API_AUTH, {JSON_API_PASSWORD,
[](app_config &cfg) { [](app_config &cfg) {
test_getter_setter(cfg, &app_config::get_api_auth, test_getter_setter(cfg, &app_config::get_api_password,
&app_config::set_api_auth, "", "auth", &app_config::set_api_password, "", "auth",
JSON_API_AUTH, "auth2"); JSON_API_PASSWORD, "auth2");
}}, }},
{JSON_API_PORT, {JSON_API_PORT,
[](app_config &cfg) { [](app_config &cfg) {

View File

@ -0,0 +1,198 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
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.
*/
#include "test_common.hpp"
#include "app_config.hpp"
#include "types/repertory.hpp"
namespace repertory {
TEST(clean_json_test, can_clean_values) {
auto result = clean_json_value(JSON_API_PASSWORD, "moose");
EXPECT_TRUE(result.empty());
result = clean_json_value(
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN),
"moose");
EXPECT_TRUE(result.empty());
result = clean_json_value(
fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD), "moose");
EXPECT_TRUE(result.empty());
result = clean_json_value(
fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
EXPECT_TRUE(result.empty());
result = clean_json_value(
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
EXPECT_TRUE(result.empty());
result = clean_json_value(
fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
EXPECT_TRUE(result.empty());
result = clean_json_value(
fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY), "moose");
EXPECT_TRUE(result.empty());
}
TEST(clean_json_test, can_clean_encrypt_config) {
auto dir =
utils::path::combine(test::get_test_output_dir(), {
"clean_json_test",
"encrypt",
});
app_config cfg(provider_type::encrypt, dir);
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN),
"moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
auto data = cfg.get_json();
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_FALSE(data.at(JSON_ENCRYPT_CONFIG)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
clean_json_config(cfg.get_provider_type(), data);
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_TRUE(data.at(JSON_ENCRYPT_CONFIG)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
}
TEST(clean_json_test, can_clean_remote_config) {
auto dir =
utils::path::combine(test::get_test_output_dir(), {
"clean_json_test",
"remote",
});
app_config cfg(provider_type::remote, dir);
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
auto data = cfg.get_json();
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_FALSE(data.at(JSON_REMOTE_CONFIG)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
clean_json_config(cfg.get_provider_type(), data);
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_TRUE(data.at(JSON_REMOTE_CONFIG)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
}
TEST(clean_json_test, can_clean_s3_config) {
auto dir =
utils::path::combine(test::get_test_output_dir(), {
"clean_json_test",
"s3",
});
app_config cfg(provider_type::s3, dir);
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
cfg.set_value_by_name(fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY),
"moose");
auto data = cfg.get_json();
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
EXPECT_FALSE(data.at(JSON_S3_CONFIG)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
EXPECT_FALSE(
data.at(JSON_S3_CONFIG).at(JSON_SECRET_KEY).get<std::string>().empty());
clean_json_config(cfg.get_provider_type(), data);
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
EXPECT_TRUE(data.at(JSON_S3_CONFIG)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
EXPECT_TRUE(
data.at(JSON_S3_CONFIG).at(JSON_SECRET_KEY).get<std::string>().empty());
}
TEST(clean_json_test, can_clean_sia_config) {
auto dir =
utils::path::combine(test::get_test_output_dir(), {
"clean_json_test",
"sia",
});
app_config cfg(provider_type::sia, dir);
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD), "moose");
cfg.set_value_by_name(
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
auto data = cfg.get_json();
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_FALSE(data.at(JSON_HOST_CONFIG)
.at(JSON_API_PASSWORD)
.get<std::string>()
.empty());
EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
clean_json_config(cfg.get_provider_type(), data);
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
EXPECT_TRUE(data.at(JSON_HOST_CONFIG)
.at(JSON_API_PASSWORD)
.get<std::string>()
.empty());
EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
.at(JSON_ENCRYPTION_TOKEN)
.get<std::string>()
.empty());
}
} // namespace repertory

View File

@ -15,6 +15,7 @@ if [ "${PROJECT_IS_MINGW}" == "1" ] && [ "${PROJECT_STATIC_LINK}" == "OFF" ]; th
/mingw64/bin/libstdc++-6.dll /mingw64/bin/libstdc++-6.dll
/mingw64/bin/libwinpthread-1.dll /mingw64/bin/libwinpthread-1.dll
/mingw64/bin/libzlib1.dll /mingw64/bin/libzlib1.dll
/mingw64/bin/libzstd.dll
/mingw64/bin/zlib1.dll /mingw64/bin/zlib1.dll
) )

View File

@ -38,14 +38,17 @@ BRANCH=$(git branch --show-current)
RELEASE=$(grep PROJECT_RELEASE_ITER= ./config.sh | sed s/PROJECT_RELEASE_ITER=//g) RELEASE=$(grep PROJECT_RELEASE_ITER= ./config.sh | sed s/PROJECT_RELEASE_ITER=//g)
popd popd
if [ "${BRANCH}" == "master" ] || [ "${BRANCH}" == "alpha" ] || [ "${BRANCH}" == "beta" ] || [ "${BRANCH}" == "rc" ]; then if [ "${BRANCH}" == "master" ] || [ "${BRANCH}" == "alpha" ] ||
[ "${BRANCH}" == "main" ] || [ "${BRANCH}" == "release" ] ||
[ "${BRANCH}" == "beta" ] || [ "${BRANCH}" == "rc" ]; then
DEST_DIR=${DEST_DIR}/${RELEASE} DEST_DIR=${DEST_DIR}/${RELEASE}
else elif [[ ${BRANCH} = *'-alpha-'* ]] || [[ ${BRANCH} = *'-beta-'* ]] ||
[[ ${BRANCH} = *'-rc-'* ]] || [[ ${BRANCH} = *'-release-'* ]]; then
DEST_DIR=${DEST_DIR}/nightly DEST_DIR=${DEST_DIR}/nightly
else
error_exit "skipping ${PROJECT_FILE_PART}" 0
fi fi
echo ${DEST_DIR}
pushd "${DIST_DIR}" pushd "${DIST_DIR}"
if [ ! -f "./${PROJECT_OUT_FILE}" ]; then if [ ! -f "./${PROJECT_OUT_FILE}" ]; then
error_exit "failed to find file: ${PROJECT_OUT_FILE}" 1 error_exit "failed to find file: ${PROJECT_OUT_FILE}" 1

View File

@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
if [ "${PROJECT_BUILD_ARCH}" == "aarch64" ]; then if [ "${PROJECT_BUILD_ARCH}" == "aarch64" ] &&
[ "${PROJECT_ENABLE_MULTIARCH_DOCKER}" == "1" ]; then
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
fi fi

View File

@ -7,6 +7,12 @@ PROJECT_IS_MINGW=$4
PROJECT_IS_MINGW_UNIX=$5 PROJECT_IS_MINGW_UNIX=$5
DISABLE_CREATE_DIRS=$6 DISABLE_CREATE_DIRS=$6
PROJECT_FLUTTER_BASE_HREF="/"
if [ "${PROJECT_IS_MINGW}" != "1" ]; then
PROJECT_IS_MINGW_UNIX=0
fi
PROJECT_SOURCE_DIR=${PROJECT_SCRIPTS_DIR}/.. PROJECT_SOURCE_DIR=${PROJECT_SCRIPTS_DIR}/..
PROJECT_SOURCE_DIR=$(realpath "${PROJECT_SOURCE_DIR}") PROJECT_SOURCE_DIR=$(realpath "${PROJECT_SOURCE_DIR}")
@ -306,9 +312,10 @@ export PROJECT_COMPANY_NAME
export PROJECT_COPYRIGHT export PROJECT_COPYRIGHT
export PROJECT_DESC export PROJECT_DESC
export PROJECT_DIST_DIR export PROJECT_DIST_DIR
export PROJECT_ENABLE_WIN32_LONG_PATH_NAMES
export PROJECT_ENABLE_V2_ERRORS export PROJECT_ENABLE_V2_ERRORS
export PROJECT_ENABLE_WIN32_LONG_PATH_NAMES
export PROJECT_FILE_PART export PROJECT_FILE_PART
export PROJECT_FLUTTER_BASE_HREF
export PROJECT_GIT_REV export PROJECT_GIT_REV
export PROJECT_IS_ALPINE export PROJECT_IS_ALPINE
export PROJECT_IS_ARM64 export PROJECT_IS_ARM64
@ -361,6 +368,7 @@ echo " Dist dir: ${PROJECT_DIST_DIR}"
echo " Enable v2 errors: ${PROJECT_ENABLE_V2_ERRORS}" echo " Enable v2 errors: ${PROJECT_ENABLE_V2_ERRORS}"
echo " External build root: ${PROJECT_EXTERNAL_BUILD_ROOT}" echo " External build root: ${PROJECT_EXTERNAL_BUILD_ROOT}"
echo " File part: ${PROJECT_FILE_PART}" echo " File part: ${PROJECT_FILE_PART}"
echo " Flutter base href: ${PROJECT_FLUTTER_BASE_HREF}"
echo " Is ARM64: ${PROJECT_IS_ARM64}" echo " Is ARM64: ${PROJECT_IS_ARM64}"
echo " Is Alpine: ${PROJECT_IS_ALPINE}" echo " Is Alpine: ${PROJECT_IS_ALPINE}"
echo " Is MINGW on Unix: ${PROJECT_IS_MINGW_UNIX}" echo " Is MINGW on Unix: ${PROJECT_IS_MINGW_UNIX}"

View File

@ -59,7 +59,7 @@ PROJECT_DOWNLOADS[PKG_CONFIG]="https://pkgconfig.freedesktop.org/releases/pkg-co
PROJECT_DOWNLOADS[PUGIXML]="https://github.com/zeux/pugixml/releases/download/v${PROJECT_VERSIONS[PUGIXML]}/pugixml-${PROJECT_VERSIONS[PUGIXML]}.tar.gz;pugixml-${PROJECT_VERSIONS[PUGIXML]}.tar.gz;3rd_party" PROJECT_DOWNLOADS[PUGIXML]="https://github.com/zeux/pugixml/releases/download/v${PROJECT_VERSIONS[PUGIXML]}/pugixml-${PROJECT_VERSIONS[PUGIXML]}.tar.gz;pugixml-${PROJECT_VERSIONS[PUGIXML]}.tar.gz;3rd_party"
PROJECT_DOWNLOADS[ROCKSDB]="https://github.com/facebook/rocksdb/archive/refs/tags/v${PROJECT_VERSIONS[ROCKSDB]}.tar.gz;rocksdb-${PROJECT_VERSIONS[ROCKSDB]}.tar.gz;3rd_party" PROJECT_DOWNLOADS[ROCKSDB]="https://github.com/facebook/rocksdb/archive/refs/tags/v${PROJECT_VERSIONS[ROCKSDB]}.tar.gz;rocksdb-${PROJECT_VERSIONS[ROCKSDB]}.tar.gz;3rd_party"
PROJECT_DOWNLOADS[SPDLOG]="https://github.com/gabime/spdlog/archive/refs/tags/v${PROJECT_VERSIONS[SPDLOG]}.tar.gz;spdlog-${PROJECT_VERSIONS[SPDLOG]}.tar.gz;3rd_party" PROJECT_DOWNLOADS[SPDLOG]="https://github.com/gabime/spdlog/archive/refs/tags/v${PROJECT_VERSIONS[SPDLOG]}.tar.gz;spdlog-${PROJECT_VERSIONS[SPDLOG]}.tar.gz;3rd_party"
PROJECT_DOWNLOADS[SQLITE]="https://www.sqlite.org/2024/sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;3rd_party" PROJECT_DOWNLOADS[SQLITE]="https://www.sqlite.org/2025/sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;3rd_party"
PROJECT_DOWNLOADS[STDUUID]="https://github.com/mariusbancila/stduuid/archive/refs/tags/v${PROJECT_VERSIONS[STDUUID]}.tar.gz;stduuid-${PROJECT_VERSIONS[STDUUID]}.tar.gz;3rd_party" PROJECT_DOWNLOADS[STDUUID]="https://github.com/mariusbancila/stduuid/archive/refs/tags/v${PROJECT_VERSIONS[STDUUID]}.tar.gz;stduuid-${PROJECT_VERSIONS[STDUUID]}.tar.gz;3rd_party"
PROJECT_DOWNLOADS[ZLIB]="https://github.com/madler/zlib/archive/refs/tags/v${PROJECT_VERSIONS[ZLIB]}.tar.gz;zlib-${PROJECT_VERSIONS[ZLIB]}.tar.gz;3rd_party/mingw64" PROJECT_DOWNLOADS[ZLIB]="https://github.com/madler/zlib/archive/refs/tags/v${PROJECT_VERSIONS[ZLIB]}.tar.gz;zlib-${PROJECT_VERSIONS[ZLIB]}.tar.gz;3rd_party/mingw64"
export PROJECT_DOWNLOADS export PROJECT_DOWNLOADS

View File

@ -18,6 +18,8 @@ fi
ln -sf "${PROJECT_BUILD_DIR}/build/compile_commands.json" "${PROJECT_SOURCE_DIR}/compile_commands.json" ln -sf "${PROJECT_BUILD_DIR}/build/compile_commands.json" "${PROJECT_SOURCE_DIR}/compile_commands.json"
pushd "${PROJECT_BUILD_DIR}" pushd "${PROJECT_BUILD_DIR}"
export CMAKE_BUILD_PARALLEL_LEVEL=${NUM_JOBS}
cmake "${PROJECT_SOURCE_DIR}" \ cmake "${PROJECT_SOURCE_DIR}" \
-G"Unix Makefiles" \ -G"Unix Makefiles" \
-DPROJECT_COMPANY_NAME="${PROJECT_COMPANY_NAME}" \ -DPROJECT_COMPANY_NAME="${PROJECT_COMPANY_NAME}" \
@ -34,10 +36,10 @@ if [ "${PROJECT_BUILD_CLEAN}" == "clean" ]; then
popd popd
fi fi
make -j${NUM_JOBS} || exit 1 make || exit 1
pushd build pushd build
make -j${NUM_JOBS} || exit 1 make -j${CMAKE_BUILD_PARALLEL_LEVEL} || exit 1
popd popd
popd popd
@ -79,6 +81,11 @@ EOF
fi fi
done done
if [ -f "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}/pubspec.yaml" ]; then
rsync -av --progress "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}/build/web/" \
"${PROJECT_DIST_DIR}/web/"
fi
if [ "${PROJECT_IS_MINGW}" == "1" ]; then if [ "${PROJECT_IS_MINGW}" == "1" ]; then
. "${PROJECT_SCRIPTS_DIR}/copy_mingw64_deps.sh" "$1" "$2" "$3" "$4" "$5" . "${PROJECT_SCRIPTS_DIR}/copy_mingw64_deps.sh" "$1" "$2" "$3" "$4" "$5"
fi fi

43
scripts/make_flutter.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
PROJECT_SCRIPTS_DIR=$(realpath "$0")
PROJECT_SCRIPTS_DIR=$(dirname "${PROJECT_SCRIPTS_DIR}")
. "${PROJECT_SCRIPTS_DIR}/env.sh" "$1" "$2" "$3" "$4" "$5"
if [ -f "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}/pubspec.yaml" ]; then
if [ "${PROJECT_IS_MINGW}" == "0" ] || [ "${PROJECT_IS_MINGW_UNIX}" == "1" ]; then
FLUTTER_CONTAINER_NAME="${PROJECT_NAME}_flutter"
FLUTTER_TAG_NAME="${PROJECT_NAME}:flutter"
docker stop ${FLUTTER_CONTAINER_NAME}
docker rm ${FLUTTER_CONTAINER_NAME}
docker build "${PROJECT_SOURCE_DIR}/docker/x86_64/" \
--build-arg UID=$(id -u) \
--build-arg GID=$(id -g) \
-t ${FLUTTER_TAG_NAME} \
-f "${PROJECT_SOURCE_DIR}/docker/x86_64/flutter" || exit 1
docker run -td \
--name ${FLUTTER_CONTAINER_NAME} \
-u $(id -u):$(id -g) \
-v ${PROJECT_SOURCE_DIR}:${PROJECT_SOURCE_DIR}:rw,z \
-w ${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME} \
${FLUTTER_TAG_NAME} || exit 1
docker exec \
${FLUTTER_CONTAINER_NAME} \
/bin/bash -c \
"flutter clean;flutter build web --base-href=${PROJECT_FLUTTER_BASE_HREF}" || exit 1
docker stop ${FLUTTER_CONTAINER_NAME}
docker rm ${FLUTTER_CONTAINER_NAME}
else
pushd "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}"
export MSYS_NO_PATHCONV=1
flutter clean
flutter build web --base-href=${PROJECT_FLUTTER_BASE_HREF} || exit 1
export MSYS_NO_PATHCONV=0
popd
fi
fi

View File

@ -9,6 +9,8 @@ if [ -f "${PROJECT_SCRIPTS_DIR}/cleanup.sh" ]; then
rm ${PROJECT_SCRIPTS_DIR}/cleanup.* rm ${PROJECT_SCRIPTS_DIR}/cleanup.*
fi fi
"${PROJECT_SOURCE_DIR}/scripts/make_flutter.sh" "${PROJECT_BUILD_ARCH}" "${PROJECT_CMAKE_BUILD_TYPE}" "${PROJECT_BUILD_CLEAN}" 0 0 || exit 1
if [ "${PROJECT_REQUIRE_ALPINE}" == "ON" ] || [ "${PROJECT_IS_ARM64}" == "1" ]; then if [ "${PROJECT_REQUIRE_ALPINE}" == "ON" ] || [ "${PROJECT_IS_ARM64}" == "1" ]; then
DOCKER_NAME=alpine DOCKER_NAME=alpine
DOCKER_CONTAINER=${PROJECT_NAME}_${DOCKER_NAME}_${PROJECT_BUILD_ARCH} DOCKER_CONTAINER=${PROJECT_NAME}_${DOCKER_NAME}_${PROJECT_BUILD_ARCH}

View File

@ -12,5 +12,7 @@ pushd "%~dp0%"
call cleanup.cmd "%ARG1%" "%ARG2%" "%ARG3%" 1 0 call cleanup.cmd "%ARG1%" "%ARG2%" "%ARG3%" 1 0
del cleanup.* del cleanup.*
) )
call mingw64 -no-start ./make_flutter.sh "%ARG1%" "%ARG2%" "%ARG3%" 1 0 || exit 1
call mingw64 -no-start ./make_common.sh "%ARG1%" "%ARG2%" "%ARG3%" 1 0 || exit 1 call mingw64 -no-start ./make_common.sh "%ARG1%" "%ARG2%" "%ARG3%" 1 0 || exit 1
popd popd

View File

@ -8,25 +8,25 @@ PROJECT_VERSIONS[BOOST2_PATCH]="0"
PROJECT_VERSIONS[BOOST_MAJOR]="1" PROJECT_VERSIONS[BOOST_MAJOR]="1"
PROJECT_VERSIONS[BOOST_MINOR]="87" PROJECT_VERSIONS[BOOST_MINOR]="87"
PROJECT_VERSIONS[BOOST_PATCH]="0" PROJECT_VERSIONS[BOOST_PATCH]="0"
PROJECT_VERSIONS[CPP_HTTPLIB]="0.18.1" PROJECT_VERSIONS[CPP_HTTPLIB]="0.19.0"
PROJECT_VERSIONS[CURL]="8.11.0" PROJECT_VERSIONS[CURL]="8.12.1"
PROJECT_VERSIONS[CURL2]="8_11_0" PROJECT_VERSIONS[CURL2]="8_12_1"
PROJECT_VERSIONS[EXPAT]="2.6.4" PROJECT_VERSIONS[EXPAT]="2.6.4"
PROJECT_VERSIONS[EXPAT2]="2_6_4" PROJECT_VERSIONS[EXPAT2]="2_6_4"
PROJECT_VERSIONS[GCC]="14.2.0" PROJECT_VERSIONS[GCC]="14.2.0"
PROJECT_VERSIONS[GTEST]="1.15.2" PROJECT_VERSIONS[GTEST]="1.16.0"
PROJECT_VERSIONS[ICU]="76-1" PROJECT_VERSIONS[ICU]="76-1"
PROJECT_VERSIONS[JSON]="3.11.3" PROJECT_VERSIONS[JSON]="3.11.3"
PROJECT_VERSIONS[LIBSODIUM]="1.0.20" PROJECT_VERSIONS[LIBSODIUM]="1.0.20"
PROJECT_VERSIONS[MESA]="23.3.3" PROJECT_VERSIONS[MESA]="23.3.3"
PROJECT_VERSIONS[MINGW]="12.0.0" PROJECT_VERSIONS[MINGW]="12.0.0"
PROJECT_VERSIONS[OPENSSL]="3.4.0" PROJECT_VERSIONS[OPENSSL]="3.4.1"
PROJECT_VERSIONS[PKG_CONFIG]="0.29.2" PROJECT_VERSIONS[PKG_CONFIG]="0.29.2"
PROJECT_VERSIONS[PUGIXML]="1.14" PROJECT_VERSIONS[PUGIXML]="1.15"
PROJECT_VERSIONS[ROCKSDB]="9.7.4" PROJECT_VERSIONS[ROCKSDB]="9.10.0"
PROJECT_VERSIONS[SPDLOG]="1.15.0" PROJECT_VERSIONS[SPDLOG]="1.15.1"
PROJECT_VERSIONS[SQLITE]="3460100" PROJECT_VERSIONS[SQLITE]="3490100"
PROJECT_VERSIONS[SQLITE2]="3.46.1" PROJECT_VERSIONS[SQLITE2]="3.49.1"
PROJECT_VERSIONS[STDUUID]="1.2.3" PROJECT_VERSIONS[STDUUID]="1.2.3"
PROJECT_VERSIONS[ZLIB]="1.3.1" PROJECT_VERSIONS[ZLIB]="1.3.1"
export PROJECT_VERSIONS export PROJECT_VERSIONS

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
405abd8170f2a446fc8612ac635d0db5947c0d2e156e32603403a4496255ff00 *cpp-httplib-0.18.1.tar.gz

BIN
support/3rd_party/cpp-httplib-0.19.0.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
c9b9e0524666e1cd088f0874c57c1ce7c0eaa8552f9f4e15c755d5201fc8c608 *cpp-httplib-0.19.0.tar.gz

Binary file not shown.

View File

@ -1 +0,0 @@
5a231145114589491fc52da118f9c7ef8abee885d1cb1ced99c7290e9a352f07 *curl-8.11.0.tar.gz

BIN
support/3rd_party/curl-8.12.1.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
6edc063d1ebaf9cf3b3b46e9fef2f3cd8932694989ecd43d005d6e828426d09f *curl-8.12.1.tar.gz

Binary file not shown.

BIN
support/3rd_party/googletest-1.16.0.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
78c676fc63881529bf97bf9d45948d905a66833fbfa5318ea2cd7478cb98f399 *googletest-1.16.0.tar.gz

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf *openssl-3.4.0.tar.gz

BIN
support/3rd_party/openssl-3.4.1.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
002a2d6b30b58bf4bea46c43bdd96365aaf8daa6c428782aa4feee06da197df3 *openssl-3.4.1.tar.gz

Binary file not shown.

View File

@ -1 +0,0 @@
2f10e276870c64b1db6809050a75e11a897a8d7456c4be5c6b2e35a11168a015 pugixml-1.14.tar.gz

BIN
support/3rd_party/pugixml-1.15.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
655ade57fa703fb421c2eb9a0113b5064bddb145d415dd1f88c79353d90d511a *pugixml-1.15.tar.gz

BIN
support/3rd_party/rocksdb-9.10.0.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
fdccab16133c9d927a183c2648bcea8d956fb41eb1df2aacaa73eb0b95e43724 *rocksdb-9.10.0.tar.gz

Binary file not shown.

View File

@ -1 +0,0 @@
9b810c81731835fda0d4bbdb51d3199d901fa4395733ab63752d297da84c5a47 *rocksdb-9.7.4.tar.gz

Binary file not shown.

View File

@ -1 +0,0 @@
9962648c9b4f1a7bbc76fd8d9172555bad1871fdb14ff4f842ef87949682caa5 *spdlog-1.15.0.tar.gz

BIN
support/3rd_party/spdlog-1.15.1.tar.gz (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
25c843860f039a1600f232c6eb9e01e6627f7d030a2ae5e232bdd3c9205d26cc *spdlog-1.15.1.tar.gz

Binary file not shown.

View File

@ -1 +0,0 @@
77823cb110929c2bcb0f5d48e4833b5c59a8a6e40cdea3936b99e199dbbe5784 sqlite-amalgamation-3460100.zip

BIN
support/3rd_party/sqlite-amalgamation-3490100.zip (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
6cebd1d8403fc58c30e93939b246f3e6e58d0765a5cd50546f16c00fd805d2c3 *sqlite-amalgamation-3490100.zip

Binary file not shown.

View File

@ -399,6 +399,13 @@ using vlc_string_t = std::unique_ptr<char, vlc_string_deleter>;
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#endif // defined(PROJECT_ENABLE_SPDLOG) #endif // defined(PROJECT_ENABLE_SPDLOG)
#if defined(PROJECT_ENABLE_FMT)
#include "fmt/chrono.h"
#include "fmt/core.h"
#include "fmt/format.h"
#include "fmt/ranges.h"
#endif // defined(PROJECT_ENABLE_FMT)
#if defined(PROJECT_ENABLE_STDUUID) #if defined(PROJECT_ENABLE_STDUUID)
#include "uuid.h" #include "uuid.h"
#endif // defined(PROJECT_ENABLE_STDUUID) #endif // defined(PROJECT_ENABLE_STDUUID)

View File

@ -131,15 +131,6 @@ inline const spdlog_exception_handler default_exception_handler{};
inline const iostream_exception_handler default_exception_handler{}; inline const iostream_exception_handler default_exception_handler{};
#endif // defined(PROJECT_ENABLE_SPDLOG) && defined(PROJECT_ENABLE_V2_ERRORS) #endif // defined(PROJECT_ENABLE_SPDLOG) && defined(PROJECT_ENABLE_V2_ERRORS)
#if defined(PROJECT_ENABLE_TESTING)
extern std::atomic<const i_exception_handler *> exception_handler;
[[nodiscard]] inline auto get_exception_handler()
-> const i_exception_handler * {
return exception_handler;
}
#endif // defined(PROJECT_ENABLE_TESTING)
#if defined(PROJECT_ENABLE_V2_ERRORS) #if defined(PROJECT_ENABLE_V2_ERRORS)
void handle_debug(std::string_view function_name, std::string_view msg); void handle_debug(std::string_view function_name, std::string_view msg);
#endif // defined(PROJECT_ENABLE_V2_ERRORS) #endif // defined(PROJECT_ENABLE_V2_ERRORS)
@ -159,6 +150,15 @@ void handle_warn(std::string_view function_name, std::string_view msg);
#endif // defined(PROJECT_ENABLE_V2_ERRORS) #endif // defined(PROJECT_ENABLE_V2_ERRORS)
void set_exception_handler(const i_exception_handler *handler); void set_exception_handler(const i_exception_handler *handler);
#if defined(PROJECT_ENABLE_TESTING)
extern std::atomic<const i_exception_handler *> exception_handler;
[[nodiscard]] inline auto get_exception_handler()
-> const i_exception_handler * {
return exception_handler;
}
#endif // defined(PROJECT_ENABLE_TESTING)
} // namespace repertory::utils::error } // namespace repertory::utils::error
#endif // REPERTORY_INCLUDE_UTILS_ERROR_HPP_ #endif // REPERTORY_INCLUDE_UTILS_ERROR_HPP_

View File

@ -131,23 +131,46 @@ auto get_next_available_port(std::uint16_t first_port,
using ip::tcp; using ip::tcp;
boost::system::error_code error_code{}; boost::system::error_code error_code{};
while (first_port != 0U) {
std::uint32_t check_port{first_port};
while (check_port <= 65535U) {
{
io_context ctx{};
tcp::socket socket(ctx);
socket.connect(
{
tcp::endpoint(ip::address_v4::loopback(),
static_cast<std::uint16_t>(check_port)),
},
error_code);
if (not error_code) {
++check_port;
continue;
}
}
{
io_context ctx{}; io_context ctx{};
tcp::acceptor acceptor(ctx); tcp::acceptor acceptor(ctx);
acceptor.open(tcp::v4(), error_code) || acceptor.open(tcp::v4(), error_code);
acceptor.bind({tcp::v4(), first_port}, error_code); if (error_code) {
if (not error_code) { ++check_port;
break; continue;
} }
++first_port; acceptor.bind({tcp::v4(), static_cast<std::uint16_t>(check_port)},
error_code);
if (error_code) {
++check_port;
continue;
}
} }
if (not error_code) { available_port = static_cast<std::uint16_t>(check_port);
available_port = first_port; return true;
} }
return not error_code; return false;
} }
#endif // defined(PROJECT_ENABLE_BOOST) #endif // defined(PROJECT_ENABLE_BOOST)

View File

@ -47,82 +47,4 @@ auto create_exception(std::string_view function_name,
return std::runtime_error(create_error_message(function_name, items)); return std::runtime_error(create_error_message(function_name, items));
} }
#if defined(PROJECT_ENABLE_V2_ERRORS)
void handle_debug(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_debug(function_name, msg);
return;
}
default_exception_handler.handle_debug(function_name, msg);
}
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
void handle_error(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_error(function_name, msg);
return;
}
default_exception_handler.handle_error(function_name, msg);
}
void handle_exception(std::string_view function_name) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_exception(function_name);
return;
}
default_exception_handler.handle_exception(function_name);
}
void handle_exception(std::string_view function_name,
const std::exception &ex) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_exception(function_name, ex);
return;
}
default_exception_handler.handle_exception(function_name, ex);
}
#if defined(PROJECT_ENABLE_V2_ERRORS)
void handle_info(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_info(function_name, msg);
return;
}
default_exception_handler.handle_info(function_name, msg);
}
void handle_trace(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_trace(function_name, msg);
return;
}
default_exception_handler.handle_trace(function_name, msg);
}
void handle_warn(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_warn(function_name, msg);
return;
}
default_exception_handler.handle_warn(function_name, msg);
}
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
void set_exception_handler(const i_exception_handler *handler) {
exception_handler = handler;
}
} // namespace repertory::utils::error } // namespace repertory::utils::error

View File

@ -19,12 +19,15 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "utils/config.hpp"
#include "utils/error.hpp" #include "utils/error.hpp"
#include "utils/config.hpp"
#include "utils/string.hpp"
namespace repertory::utils::error { namespace repertory::utils::error {
std::atomic<const i_exception_handler *> exception_handler{ std::atomic<const i_exception_handler *> exception_handler{
&default_exception_handler}; &default_exception_handler,
};
#if defined(PROJECT_ENABLE_V2_ERRORS) #if defined(PROJECT_ENABLE_V2_ERRORS)
void iostream_exception_handler::handle_debug(std::string_view function_name, void iostream_exception_handler::handle_debug(std::string_view function_name,
@ -238,4 +241,83 @@ void spdlog_exception_handler::handle_warn(std::string_view function_name,
file->warn(utils::error::create_error_message(function_name, {msg})); file->warn(utils::error::create_error_message(function_name, {msg}));
} }
#endif // defined(PROJECT_ENABLE_SPDLOG) && defined(PROJECT_ENABLE_V2_ERRORS) #endif // defined(PROJECT_ENABLE_SPDLOG) && defined(PROJECT_ENABLE_V2_ERRORS)
#if defined(PROJECT_ENABLE_V2_ERRORS)
void handle_debug(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_debug(function_name, msg);
return;
}
default_exception_handler.handle_debug(function_name, msg);
}
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
void handle_error(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_error(function_name, msg);
return;
}
default_exception_handler.handle_error(function_name, msg);
}
void handle_exception(std::string_view function_name) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_exception(function_name);
return;
}
default_exception_handler.handle_exception(function_name);
}
void handle_exception(std::string_view function_name,
const std::exception &ex) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_exception(function_name, ex);
return;
}
default_exception_handler.handle_exception(function_name, ex);
}
#if defined(PROJECT_ENABLE_V2_ERRORS)
void handle_info(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_info(function_name, msg);
return;
}
default_exception_handler.handle_info(function_name, msg);
}
void handle_trace(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_trace(function_name, msg);
return;
}
default_exception_handler.handle_trace(function_name, msg);
}
void handle_warn(std::string_view function_name, std::string_view msg) {
const i_exception_handler *handler{exception_handler};
if (handler != nullptr) {
handler->handle_warn(function_name, msg);
return;
}
default_exception_handler.handle_warn(function_name, msg);
}
#endif // defined(PROJECT_ENABLE_V2_ERRORS)
void set_exception_handler(const i_exception_handler *handler) {
exception_handler = handler;
}
} // namespace repertory::utils::error } // namespace repertory::utils::error

View File

@ -0,0 +1,8 @@
autofocus
canvaskit
cupertino
cupertinoicons
fromargb
onetwothree
renterd
rocksdb

47
web/repertory/.gitignore vendored Normal file
View File

@ -0,0 +1,47 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.flutter-companion
pubspec.lock

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