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
21 changed files with 436 additions and 578 deletions

View File

@ -115,7 +115,6 @@ googletest
gpath gpath
gtest_version gtest_version
has_setxattr has_setxattr
hkey
httpapi httpapi
httplib httplib
icudata icudata
@ -145,7 +144,6 @@ libuuid_include_dirs
libvlc libvlc
linkflags linkflags
localappdata localappdata
lpbyte
lptr lptr
lpwstr lpwstr
markdownlint markdownlint

View File

@ -4,14 +4,15 @@
### Issues ### 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 * \#39 Create management portal in Flutter
### Changes from v2.0.4-rc ### Changes from v2.0.4-rc
* Continue documentation updates * Continue documentation updates
* Fixed `-status` command erasing active mount information * Prevent overlapping `repertory` `ApiPort`'s
* Fixed overlapping HTTP REST API port's
* Refactored/fixed instance locking
* Removed passwords and secret key values from API calls * Removed passwords and secret key values from API calls
* Renamed setting `ApiAuth` to `ApiPassword` * Renamed setting `ApiAuth` to `ApiPassword`
* Require `--name,-na` option for encryption provider * Require `--name,-na` option for encryption provider

View File

@ -22,13 +22,6 @@
#ifndef REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_ #ifndef REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
#define REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_ #define REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
#include "types/repertory.hpp"
namespace repertory {
[[nodiscard]] auto create_lock_id(provider_type prov,
std::string_view unique_id)->std::string;
}
#if defined(_WIN32) #if defined(_WIN32)
#include "platform/win32_platform.hpp" #include "platform/win32_platform.hpp"
#include "utils/windows.hpp" #include "utils/windows.hpp"

View File

@ -30,44 +30,40 @@ class i_provider;
class lock_data final { class lock_data final {
public: public:
lock_data(provider_type prov, std::string_view unique_id); explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/);
lock_data(const lock_data &) = delete; lock_data();
lock_data(lock_data &&) = delete;
auto operator=(const lock_data &) -> lock_data & = delete;
auto operator=(lock_data &&) -> lock_data & = delete;
~lock_data(); ~lock_data();
private: private:
std::string mutex_id_; const provider_type pt_;
const std::string unique_id_;
private: const std::string mutex_id_;
int handle_{}; 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;
[[nodiscard]] auto get_lock_data_file() const -> std::string; [[nodiscard]] static auto get_lock_data_file() -> std::string;
[[nodiscard]] auto get_lock_file() const -> std::string; [[nodiscard]] auto get_lock_file() -> std::string;
private: private:
[[nodiscard]] static auto wait_for_lock(int handle, [[nodiscard]] static auto wait_for_lock(int fd,
std::uint8_t retry_count = 30U) std::uint8_t retry_count = 30u)
-> int; -> 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(); void release();
[[nodiscard]] auto set_mount_state(bool active, [[nodiscard]] auto set_mount_state(bool active,
std::string_view mount_location, int pid) const std::string &mount_location, int pid)
-> bool; -> bool;
}; };
@ -83,5 +79,5 @@ public:
const api_file &file) -> api_error; const api_file &file) -> api_error;
} // namespace repertory } // namespace repertory
#endif // !defined(_WIN32) #endif // _WIN32
#endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_ #endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_

View File

@ -23,6 +23,7 @@
#define REPERTORY_INCLUDE_PLATFORM_WINPLATFORM_HPP_ #define REPERTORY_INCLUDE_PLATFORM_WINPLATFORM_HPP_
#if defined(_WIN32) #if defined(_WIN32)
#include "app_config.hpp"
#include "types/repertory.hpp" #include "types/repertory.hpp"
namespace repertory { namespace repertory {
@ -30,32 +31,43 @@ class i_provider;
class lock_data final { class lock_data final {
public: public:
explicit lock_data(provider_type prov, std::string unique_id); explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
lock_data(const lock_data &) = delete; : pt_(pt),
lock_data(lock_data &&) = delete; unique_id_(std::move(unique_id)),
mutex_id_("repertory_" + app_config::get_provider_name(pt) + "_" +
unique_id_),
mutex_handle_(::CreateMutex(nullptr, FALSE, &mutex_id_[0u])) {}
~lock_data(); lock_data()
: pt_(provider_type::sia),
unique_id_(""),
mutex_id_(""),
mutex_handle_(INVALID_HANDLE_VALUE) {}
auto operator=(const lock_data &) -> lock_data & = delete; ~lock_data() { release(); }
auto operator=(lock_data &&) -> lock_data & = delete;
private: private:
std::string mutex_id_; const provider_type pt_;
HANDLE mutex_handle_{INVALID_HANDLE_VALUE}; const std::string unique_id_;
DWORD mutex_state_{WAIT_FAILED}; const std::string mutex_id_;
HANDLE mutex_handle_;
[[nodiscard]] auto get_current_mount_state(json &mount_state) -> bool; DWORD mutex_state_ = WAIT_FAILED;
public: public:
[[nodiscard]] auto get_mount_state(const provider_type &pt,
json &mount_state) -> bool;
[[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 get_unique_id() const -> std::string { return unique_id_; }
[[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30) -> lock_result;
void release(); void release();
[[nodiscard]] auto set_mount_state(bool active, [[nodiscard]] auto set_mount_state(bool active,
std::string_view mount_location, const std::string &mount_location,
std::int64_t pid) -> bool; const std::int64_t &pid) -> bool;
}; };
[[nodiscard]] auto create_meta_attributes( [[nodiscard]] auto create_meta_attributes(

View File

@ -21,8 +21,9 @@
*/ */
#if !defined(_WIN32) #if !defined(_WIN32)
#include "platform/platform.hpp" #include "platform/unix_platform.hpp"
#include "app_config.hpp"
#include "events/event_system.hpp" #include "events/event_system.hpp"
#include "events/types/filesystem_item_added.hpp" #include "events/types/filesystem_item_added.hpp"
#include "providers/i_provider.hpp" #include "providers/i_provider.hpp"
@ -35,65 +36,52 @@
#include "utils/unix.hpp" #include "utils/unix.hpp"
namespace repertory { namespace repertory {
lock_data::lock_data(provider_type prov, std::string_view unique_id) lock_data::lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
: mutex_id_(create_lock_id(prov, unique_id)) { : pt_(pt),
handle_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); unique_id_(std::move(unique_id)),
mutex_id_("repertory_" + app_config::get_provider_name(pt) + "_" +
unique_id_) {
lock_fd_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
} }
lock_data::lock_data()
: pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {}
lock_data::~lock_data() { release(); } lock_data::~lock_data() { release(); }
auto lock_data::get_lock_data_file() const -> std::string { auto lock_data::get_lock_data_file() -> std::string {
auto dir = get_state_directory(); const auto dir = get_state_directory();
if (not utils::file::directory(dir).create_directory()) { if (not utils::file::directory(dir).create_directory()) {
throw startup_exception("failed to create directory|sp|" + dir + "|err|" + throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
std::to_string(utils::get_last_error_code())); std::to_string(utils::get_last_error_code()));
} }
return utils::path::combine( return utils::path::combine(
dir, { dir, {"mountstate_" + std::to_string(getuid()) + ".json"});
fmt::format("{}_{}.json", mutex_id_, getuid()),
});
} }
auto lock_data::get_lock_file() const -> std::string { auto lock_data::get_lock_file() -> std::string {
auto dir = get_state_directory(); const auto dir = get_state_directory();
if (not utils::file::directory(dir).create_directory()) { if (not utils::file::directory(dir).create_directory()) {
throw startup_exception("failed to create directory|sp|" + dir + "|err|" + throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
std::to_string(utils::get_last_error_code())); std::to_string(utils::get_last_error_code()));
} }
return utils::path::combine( return utils::path::combine(dir,
dir, { {mutex_id_ + "_" + std::to_string(getuid())});
fmt::format("{}_{}.lock", mutex_id_, getuid()),
});
} }
auto lock_data::get_mount_state(json &mount_state) -> bool { auto lock_data::get_mount_state(json &mount_state) -> bool {
auto handle = open(get_lock_data_file().c_str(), O_RDWR, S_IWUSR | S_IRUSR); auto ret = false;
if (handle == -1) { auto fd =
mount_state = { open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
{"Active", false}, if (fd != -1) {
{"Location", ""}, if (wait_for_lock(fd) == 0) {
{"PID", -1},
};
return true;
}
auto ret{false};
if (wait_for_lock(handle) == 0) {
ret = utils::file::read_json_file(get_lock_data_file(), mount_state); ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
if (ret && mount_state.empty()) { flock(fd, LOCK_UN);
mount_state = {
{"Active", false},
{"Location", ""},
{"PID", -1},
};
}
flock(handle, LOCK_UN);
} }
close(handle); close(fd);
}
return ret; return ret;
} }
@ -101,20 +89,25 @@ auto lock_data::get_state_directory() -> std::string {
#if defined(__APPLE__) #if defined(__APPLE__)
return utils::path::absolute("~/Library/Application Support/" + return utils::path::absolute("~/Library/Application Support/" +
std::string{REPERTORY_DATA_NAME} + "/state"); std::string{REPERTORY_DATA_NAME} + "/state");
#else // !defined(__APPLE__) #else
return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} + return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} +
"/state"); "/state");
#endif // defined(__APPLE__) #endif
} }
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result { auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
if (handle_ == -1) { REPERTORY_USES_FUNCTION_NAME();
if (lock_fd_ == -1) {
return lock_result::failure; return lock_result::failure;
} }
lock_status_ = wait_for_lock(handle_, retry_count); lock_status_ = wait_for_lock(lock_fd_, retry_count);
switch (lock_status_) { switch (lock_status_) {
case 0: case 0:
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(function_name, "failed to set mount state");
}
return lock_result::success; return lock_result::success;
case EWOULDBLOCK: case EWOULDBLOCK:
return lock_result::locked; return lock_result::locked;
@ -124,53 +117,55 @@ auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
} }
void lock_data::release() { void lock_data::release() {
if (handle_ == -1) { if (lock_fd_ == -1) {
return; return;
} }
if (lock_status_ == 0) { if (lock_status_ == 0) {
[[maybe_unused]] auto success{utils::file::file{get_lock_file()}.remove()}; unlink(get_lock_file().c_str());
flock(handle_, LOCK_UN); flock(lock_fd_, LOCK_UN);
} }
close(handle_); close(lock_fd_);
handle_ = -1; lock_fd_ = -1;
} }
auto lock_data::set_mount_state(bool active, std::string_view 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();
auto ret = false;
auto handle = auto handle =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (handle == -1) { if (handle != -1) {
return false;
}
auto ret{false};
if (wait_for_lock(handle) == 0) { if (wait_for_lock(handle) == 0) {
const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
json mount_state; json mount_state;
if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) { if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
utils::error::raise_error(function_name, utils::error::raise_error(function_name,
"failed to read mount state file|sp|" + "failed to read mount state file|sp|" +
get_lock_file()); get_lock_file());
} }
if ((mount_state.find("Active") == mount_state.end()) || if ((mount_state.find(mount_id) == mount_state.end()) ||
(mount_state["Active"].get<bool>() != active) || (mount_state[mount_id].find("Active") ==
(active && mount_state[mount_id].end()) ||
((mount_state.find("Location") == mount_state.end()) || (mount_state[mount_id]["Active"].get<bool>() != active) ||
(mount_state["Location"].get<std::string>() != mount_location)))) { (active && ((mount_state[mount_id].find("Location") ==
if (mount_location.empty() && not active) { mount_state[mount_id].end()) ||
ret = utils::file::file{get_lock_data_file()}.remove(); (mount_state[mount_id]["Location"].get<std::string>() !=
} else { mount_location)))) {
ret = utils::file::write_json_file( const auto lines = utils::file::read_file_lines(get_lock_data_file());
get_lock_data_file(), const auto txt = std::accumulate(
{ lines.begin(), lines.end(), std::string(),
[](auto &&val, auto &&line) -> auto { return val + line; });
auto json_data = json::parse(txt.empty() ? "{}" : txt);
json_data[mount_id] = {
{"Active", active}, {"Active", active},
{"Location", active ? mount_location : ""}, {"Location", active ? mount_location : ""},
{"PID", active ? pid : -1}, {"PID", active ? pid : -1},
}); };
} ret = utils::file::write_json_file(get_lock_data_file(), json_data);
} else { } else {
ret = true; ret = true;
} }
@ -179,16 +174,17 @@ auto lock_data::set_mount_state(bool active, std::string_view mount_location,
} }
close(handle); close(handle);
}
return ret; return ret;
} }
auto lock_data::wait_for_lock(int handle, std::uint8_t retry_count) -> int { auto lock_data::wait_for_lock(int fd, std::uint8_t retry_count) -> int {
static constexpr const std::uint32_t max_sleep{100U}; static constexpr const std::uint32_t max_sleep = 100U;
auto lock_status{EWOULDBLOCK}; auto lock_status = EWOULDBLOCK;
auto remain{static_cast<std::uint32_t>(retry_count * max_sleep)}; auto remain = static_cast<std::uint32_t>(retry_count * max_sleep);
while ((remain > 0) && (lock_status == EWOULDBLOCK)) { while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
lock_status = flock(handle, LOCK_EX | LOCK_NB); lock_status = flock(fd, LOCK_EX | LOCK_NB);
if (lock_status == -1) { if (lock_status == -1) {
lock_status = errno; lock_status = errno;
if (lock_status == EWOULDBLOCK) { if (lock_status == EWOULDBLOCK) {
@ -237,13 +233,13 @@ auto provider_meta_handler(i_provider &provider, bool directory,
const api_file &file) -> api_error { const api_file &file) -> api_error {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
auto meta = create_meta_attributes( const auto meta = create_meta_attributes(
file.accessed_date, file.accessed_date,
directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE, directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE,
file.changed_date, file.creation_date, directory, getgid(), file.key, file.changed_date, file.creation_date, directory, getgid(), file.key,
directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR
: S_IFREG | S_IRUSR | S_IWUSR, : S_IFREG | S_IRUSR | S_IWUSR,
file.modified_date, 0U, 0U, file.file_size, file.source_path, getuid(), file.modified_date, 0u, 0u, file.file_size, file.source_path, getuid(),
file.modified_date); file.modified_date);
auto res = provider.set_item_meta(file.api_path, meta); auto res = provider.set_item_meta(file.api_path, meta);
if (res == api_error::success) { if (res == api_error::success) {

View File

@ -21,171 +21,150 @@
*/ */
#if defined(_WIN32) #if defined(_WIN32)
#include "platform/platform.hpp" #include "platform/win32_platform.hpp"
#include "events/event_system.hpp" #include "events/event_system.hpp"
#include "events/types/filesystem_item_added.hpp" #include "events/types/filesystem_item_added.hpp"
#include "providers/i_provider.hpp" #include "providers/i_provider.hpp"
#include "utils/config.hpp"
#include "utils/error_utils.hpp" #include "utils/error_utils.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
namespace repertory { namespace repertory {
lock_data::lock_data(provider_type prov, std::string unique_id) auto lock_data::get_mount_state(const provider_type & /*pt*/, json &mount_state)
: mutex_id_(create_lock_id(prov, unique_id)), -> bool {
mutex_handle_(::CreateMutex(nullptr, FALSE, const auto ret = get_mount_state(mount_state);
create_lock_id(prov, unique_id).c_str())) {} if (ret) {
const auto mount_id =
lock_data::~lock_data() { release(); } app_config::get_provider_display_name(pt_) + unique_id_;
mount_state = mount_state[mount_id].empty()
auto lock_data::get_current_mount_state(json &mount_state) -> bool { ? json({{"Active", false}, {"Location", ""}, {"PID", -1}})
REPERTORY_USES_FUNCTION_NAME(); : mount_state[mount_id];
HKEY key{};
if (::RegOpenKeyEx(HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts\{})",
REPERTORY_DATA_NAME, mutex_id_)
.c_str(),
0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
return true;
} }
std::string data;
DWORD data_size{};
DWORD type{REG_SZ};
::RegGetValueA(key, nullptr, nullptr, RRF_RT_REG_SZ, &type, nullptr,
&data_size);
data.resize(data_size);
auto res = ::RegGetValueA(key, nullptr, nullptr, RRF_RT_REG_SZ, &type,
data.data(), &data_size);
auto ret = res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND;
if (ret && data_size != 0U) {
try {
mount_state = json::parse(data);
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e, "failed to read mount state");
ret = false;
}
}
::RegCloseKey(key);
return ret; return ret;
} }
auto lock_data::get_mount_state(json &mount_state) -> bool { auto lock_data::get_mount_state(json &mount_state) -> bool {
if (not get_current_mount_state(mount_state)) { HKEY key;
return false; auto ret = !::RegCreateKeyEx(
HKEY_CURRENT_USER,
("SOFTWARE\\" + std::string{REPERTORY_DATA_NAME} + "\\Mounts").c_str(), 0,
nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr);
if (ret) {
DWORD i = 0u;
DWORD data_size = 0u;
std::string name;
name.resize(32767u);
auto name_size = static_cast<DWORD>(name.size());
while (ret &&
(::RegEnumValue(key, i, &name[0], &name_size, nullptr, nullptr,
nullptr, &data_size) == ERROR_SUCCESS)) {
std::string data;
data.resize(data_size);
name_size++;
if ((ret = !::RegEnumValue(key, i++, &name[0], &name_size, nullptr,
nullptr, reinterpret_cast<LPBYTE>(&data[0]),
&data_size))) {
mount_state[name.c_str()] = json::parse(data);
name_size = static_cast<DWORD>(name.size());
data_size = 0u;
} }
}
mount_state = mount_state.empty() ? json({ ::RegCloseKey(key);
{"Active", false}, }
{"Location", ""}, return ret;
{"PID", -1},
})
: mount_state;
return true;
} }
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result { auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
static constexpr const std::uint32_t max_sleep{100U}; REPERTORY_USES_FUNCTION_NAME();
auto ret = lock_result::success;
if (mutex_handle_ == INVALID_HANDLE_VALUE) { if (mutex_handle_ == INVALID_HANDLE_VALUE) {
return lock_result::failure; ret = lock_result::failure;
} } else {
for (auto i = 0;
for (std::uint8_t idx = 0U; (i <= retry_count) && ((mutex_state_ = ::WaitForSingleObject(
(idx <= retry_count) && mutex_handle_, 100)) == WAIT_TIMEOUT);
((mutex_state_ = ::WaitForSingleObject(mutex_handle_, max_sleep)) == i++) {
WAIT_TIMEOUT);
++idx) {
} }
switch (mutex_state_) { switch (mutex_state_) {
case WAIT_OBJECT_0: case WAIT_OBJECT_0: {
return lock_result::success; ret = lock_result::success;
auto should_reset = true;
json mount_state;
if (get_mount_state(pt_, mount_state)) {
if (mount_state["Active"].get<bool>() &&
mount_state["Location"] == "elevating") {
should_reset = false;
}
}
if (should_reset) {
if (not set_mount_state(false, "", -1)) {
utils::error::raise_error(function_name, "failed to set mount state");
}
}
} break;
case WAIT_TIMEOUT: case WAIT_TIMEOUT:
return lock_result::locked; ret = lock_result::locked;
break;
default: default:
return lock_result::failure; ret = lock_result::failure;
break;
} }
}
return ret;
} }
void lock_data::release() { void lock_data::release() {
if (mutex_handle_ == INVALID_HANDLE_VALUE) { if (mutex_handle_ != INVALID_HANDLE_VALUE) {
return;
}
if ((mutex_state_ == WAIT_OBJECT_0) || (mutex_state_ == WAIT_ABANDONED)) { if ((mutex_state_ == WAIT_OBJECT_0) || (mutex_state_ == WAIT_ABANDONED)) {
if (mutex_state_ == WAIT_OBJECT_0) {
[[maybe_unused]] auto success{set_mount_state(false, "", -1)};
}
::ReleaseMutex(mutex_handle_); ::ReleaseMutex(mutex_handle_);
} }
::CloseHandle(mutex_handle_); ::CloseHandle(mutex_handle_);
mutex_handle_ = INVALID_HANDLE_VALUE; mutex_handle_ = INVALID_HANDLE_VALUE;
}
} }
auto lock_data::set_mount_state(bool active, std::string_view mount_location, auto lock_data::set_mount_state(bool active, const std::string &mount_location,
std::int64_t pid) -> bool { const std::int64_t &pid) -> bool {
if (mutex_handle_ == INVALID_HANDLE_VALUE) { auto ret = false;
return false; if (mutex_handle_ != INVALID_HANDLE_VALUE) {
} const auto mount_id =
app_config::get_provider_display_name(pt_) + unique_id_;
json mount_state; json mount_state;
[[maybe_unused]] auto success{get_mount_state(mount_state)}; [[maybe_unused]] auto success = get_mount_state(mount_state);
if (not((mount_state.find("Active") == mount_state.end()) || if ((mount_state.find(mount_id) == mount_state.end()) ||
(mount_state["Active"].get<bool>() != active) || (mount_state[mount_id].find("Active") == mount_state[mount_id].end()) ||
(active && (mount_state[mount_id]["Active"].get<bool>() != active) ||
((mount_state.find("Location") == mount_state.end()) || (active && ((mount_state[mount_id].find("Location") ==
(mount_state["Location"].get<std::string>() != mount_location))))) { mount_state[mount_id].end()) ||
return true; (mount_state[mount_id]["Location"].get<std::string>() !=
} mount_location)))) {
HKEY key;
HKEY key{}; if ((ret = !::RegCreateKeyEx(
if (::RegCreateKeyExA(HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts\{})",
REPERTORY_DATA_NAME, mutex_id_)
.c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
nullptr) != ERROR_SUCCESS) {
return false;
}
auto ret{false};
if (mount_location.empty() && not active) {
::RegCloseKey(key);
if (::RegCreateKeyExA(
HKEY_CURRENT_USER, HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts)", REPERTORY_DATA_NAME).c_str(), ("SOFTWARE\\" + std::string{REPERTORY_DATA_NAME} + "\\Mounts")
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, .c_str(),
nullptr) != ERROR_SUCCESS) { 0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr))) {
return false; const auto str = json({{"Active", active},
}
ret = (::RegDeleteKeyA(key, mutex_id_.c_str()) == ERROR_SUCCESS);
} else {
auto data{
json({
{"Active", active},
{"Location", active ? mount_location : ""}, {"Location", active ? mount_location : ""},
{"PID", active ? pid : -1}, {"PID", active ? pid : -1}})
}) .dump(0);
.dump(), ret = !::RegSetValueEx(key, &mount_id[0], 0, REG_SZ,
}; reinterpret_cast<const BYTE *>(&str[0]),
ret = (::RegSetValueEx(key, nullptr, 0, REG_SZ, static_cast<DWORD>(str.size()));
reinterpret_cast<const BYTE *>(data.c_str()), ::RegCloseKey(key);
static_cast<DWORD>(data.size())) == ERROR_SUCCESS); }
} else {
ret = true;
}
} }
::RegCloseKey(key);
return ret; return ret;
} }
@ -236,4 +215,4 @@ auto provider_meta_handler(i_provider &provider, bool directory,
} }
} // namespace repertory } // namespace repertory
#endif // defined(_WIN32) #endif //_WIN32

View File

@ -29,7 +29,9 @@
#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 "rpc/common.hpp"
#include "utils/base64.hpp"
#include "utils/error_utils.hpp" #include "utils/error_utils.hpp"
#include "utils/string.hpp"
namespace repertory { namespace repertory {
server::server(app_config &config) : config_(config) {} server::server(app_config &config) : config_(config) {}
@ -142,17 +144,13 @@ void server::start() {
initialize(*server_); initialize(*server_);
server_thread_ = std::make_unique<std::thread>([this]() { server_thread_ = std::make_unique<std::thread>([this]() {
#ifdef _WIN32
server_->set_socket_options([](auto &&sock) { server_->set_socket_options([](auto &&sock) {
#if defined(_WIN32) int enable = 1;
int enable{1};
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *>(&enable), sizeof(enable)); reinterpret_cast<const char *>(&enable), sizeof(enable));
#else // !defined(_WIN32)
linger opt{1, 0};
setsockopt(sock, SOL_SOCKET, SO_LINGER,
reinterpret_cast<const char *>(&opt), sizeof(opt));
#endif // defined(_WIN32)
}); });
#endif // _WIN32
server_->listen("127.0.0.1", config_.get_api_port()); server_->listen("127.0.0.1", config_.get_api_port());
}); });

View File

@ -1,31 +0,0 @@
/*
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 "platform/platform.hpp"
#include "app_config.hpp"
namespace repertory {
auto create_lock_id(provider_type prov, std::string_view unique_id)->std::string {
return fmt::format("{}_{}_{}", REPERTORY_DATA_NAME,
app_config::get_provider_name(prov), unique_id);
}
} // namespace repertory

View File

@ -28,13 +28,13 @@
#include "providers/provider.hpp" #include "providers/provider.hpp"
#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/file.hpp" #include "utils/file.hpp"
#if defined(_WIN32) #if defined(_WIN32)
#include "drives/winfsp/remotewinfsp/remote_client.hpp" #include "drives/winfsp/remotewinfsp/remote_client.hpp"
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp" #include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
#include "drives/winfsp/winfsp_drive.hpp" #include "drives/winfsp/winfsp_drive.hpp"
#include "utils/com_init_wrapper.hpp"
using repertory_drive = repertory::winfsp_drive; using repertory_drive = repertory::winfsp_drive;
using remote_client = repertory::remote_winfsp::remote_client; using remote_client = repertory::remote_winfsp::remote_client;
@ -56,15 +56,6 @@ 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 {
lock_data global_lock(provider_type::unknown, "global");
{
auto 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;
}
}
lock_data lock(prov, unique_id); lock_data lock(prov, unique_id);
auto lock_result = lock.grab_lock(); auto lock_result = lock.grab_lock();
if (lock_result == lock_result::locked) { if (lock_result == lock_result::locked) {
@ -106,6 +97,13 @@ mount(std::vector<const char *> args, std::string data_directory,
} }
#endif // defined(_WIN32) #endif // defined(_WIN32)
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); auto drive_args = utils::cli::parse_drive_options(args, prov, data_directory);
app_config config(prov, data_directory); app_config config(prov, data_directory);
{ {

View File

@ -74,7 +74,7 @@ private:
[[nodiscard]] auto data_directory_exists(provider_type prov, [[nodiscard]] auto data_directory_exists(provider_type prov,
std::string_view name) const -> bool; std::string_view name) const -> bool;
static void handle_get_available_locations(httplib::Response &res); void handle_get_available_locations(httplib::Response &res) const;
void handle_get_mount(const httplib::Request &req, void handle_get_mount(const httplib::Request &req,
httplib::Response &res) const; httplib::Response &res) const;
@ -96,9 +96,6 @@ private:
void handle_post_mount(const httplib::Request &req, httplib::Response &res); void handle_post_mount(const httplib::Request &req, httplib::Response &res);
void handle_put_mount_location(const httplib::Request &req,
httplib::Response &res) const;
void handle_put_set_value_by_name(const httplib::Request &req, void handle_put_set_value_by_name(const httplib::Request &req,
httplib::Response &res) const; httplib::Response &res) const;

View File

@ -109,17 +109,13 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
server_(server) { server_(server) {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
#ifdef _WIN32
server_->set_socket_options([](auto &&sock) { server_->set_socket_options([](auto &&sock) {
#if defined(_WIN32) int enable = 1;
int enable{1};
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char *>(&enable), sizeof(enable)); reinterpret_cast<const char *>(&enable), sizeof(enable));
#else // !defined(_WIN32)
linger opt{1, 0};
setsockopt(sock, SOL_SOCKET, SO_LINGER,
reinterpret_cast<const char *>(&opt), sizeof(opt));
#endif // defined(_WIN32)
}); });
#endif // _WIN32
server_->set_pre_routing_handler( server_->set_pre_routing_handler(
[this](const httplib::Request &req, [this](const httplib::Request &req,
@ -172,7 +168,7 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
: http_error_codes::internal_error; : http_error_codes::internal_error;
}); });
server->Get("/api/v1/locations", [](auto && /* req */, auto &&res) { server->Get("/api/v1/locations", [this](auto && /* req */, auto &&res) {
handle_get_available_locations(res); handle_get_available_locations(res);
}); });
@ -205,10 +201,6 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
server->Post("/api/v1/mount", server->Post("/api/v1/mount",
[this](auto &&req, auto &&res) { handle_post_mount(req, res); }); [this](auto &&req, auto &&res) { handle_post_mount(req, res); });
server->Put("/api/v1/mount_location", [this](auto &&req, auto &&res) {
handle_put_mount_location(req, res);
});
server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) { server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
handle_put_set_value_by_name(req, res); handle_put_set_value_by_name(req, res);
}); });
@ -306,24 +298,7 @@ auto handlers::data_directory_exists(provider_type prov,
return ret; return ret;
} }
void handlers::handle_put_mount_location(const httplib::Request &req, void handlers::handle_get_available_locations(httplib::Response &res) const {
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");
auto location = req.get_param_value("location");
if (not data_directory_exists(prov, name)) {
res.status = http_error_codes::not_found;
return;
}
config_->set_mount_location(prov, name, location);
res.status = http_error_codes::ok;
}
void handlers::handle_get_available_locations(httplib::Response &res) {
#if defined(_WIN32) #if defined(_WIN32)
constexpr const std::array<std::string_view, 26U> letters{ constexpr const std::array<std::string_view, 26U> letters{
"A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:", "A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:",
@ -432,6 +407,8 @@ void handlers::handle_get_mount_location(const httplib::Request &req,
void handlers::handle_get_mount_status(const httplib::Request &req, void handlers::handle_get_mount_status(const httplib::Request &req,
httplib::Response &res) const { httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
auto name = req.get_param_value("name"); auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type")); auto prov = provider_type_from_string(req.get_param_value("type"));
@ -440,9 +417,34 @@ void handlers::handle_get_mount_status(const httplib::Request &req,
return; 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"}); auto lines = launch_process(prov, name, {"-status"});
auto result = nlohmann::json::parse(utils::string::join(lines, '\n')); nlohmann::json result(
nlohmann::json::parse(utils::string::join(lines, '\n')).at(status_name));
if (result.at("Location").get<std::string>().empty()) { if (result.at("Location").get<std::string>().empty()) {
result.at("Location") = config_->get_mount_location(prov, name); result.at("Location") = config_->get_mount_location(prov, name);
} else if (result.at("Active").get<bool>()) { } else if (result.at("Active").get<bool>()) {
@ -488,14 +490,14 @@ void handlers::handle_post_add_mount(const httplib::Request &req,
for (const auto &[key, value] : cfg.items()) { for (const auto &[key, value] : cfg.items()) {
if (value.is_object()) { if (value.is_object()) {
for (const auto &[key2, value2] : value.items()) { for (const auto &[key2, value2] : value.items()) {
auto sub_key = fmt::format("{}.{}", key, key2); auto subKey = fmt::format("{}.{}", key, key2);
auto skip{false}; auto skip{false};
auto decrypted = decrypt_value( auto decrypted = decrypt_value(
config_, sub_key, value2.template get<std::string>(), skip); config_, subKey, value2.template get<std::string>(), skip);
if (skip) { if (skip) {
continue; continue;
} }
values[sub_key] = decrypted; values[subKey] = decrypted;
} }
continue; continue;
@ -544,12 +546,12 @@ void handlers::handle_post_mount(const httplib::Request &req,
return; return;
} }
config_->set_mount_location(prov, name, location);
static std::mutex mount_mtx; static std::mutex mount_mtx;
mutex_lock lock(mount_mtx); mutex_lock lock(mount_mtx);
launch_process(prov, name, {location}, true); launch_process(prov, name, {location}, true);
config_->set_mount_location(prov, name, location);
launch_process(prov, name, {"-status"}); launch_process(prov, name, {"-status"});
} }
@ -580,7 +582,7 @@ void handlers::handle_put_set_value_by_name(const httplib::Request &req,
void handlers::handle_put_settings(const httplib::Request &req, void handlers::handle_put_settings(const httplib::Request &req,
httplib::Response &res) const { httplib::Response &res) const {
auto data = nlohmann::json::parse(req.get_param_value("data")); nlohmann::json data = nlohmann::json::parse(req.get_param_value("data"));
if (data.contains(JSON_API_PASSWORD)) { if (data.contains(JSON_API_PASSWORD)) {
auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(), auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),
@ -659,8 +661,10 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
args.insert(std::next(args.begin(), 3U), ""); args.insert(std::next(args.begin(), 3U), "");
args.insert(std::next(args.begin(), 4U), "/MIN"); args.insert(std::next(args.begin(), 4U), "/MIN");
args.insert(std::next(args.begin(), 5U), repertory_binary_); args.insert(std::next(args.begin(), 5U), repertory_binary_);
#else // !defined(_WIN32) #elif defined(__linux__) // defined(__linux__)
args.insert(args.begin(), repertory_binary_); args.insert(args.begin(), repertory_binary_);
#else // !defined(__linux__) && !defined(_WIN32)
build fails here
#endif // defined(_WIN32) #endif // defined(_WIN32)
std::vector<const char *> exec_args; std::vector<const char *> exec_args;
@ -673,9 +677,16 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
#if defined(_WIN32) #if defined(_WIN32)
_spawnv(_P_DETACH, exec_args.at(0U), _spawnv(_P_DETACH, exec_args.at(0U),
const_cast<char *const *>(exec_args.data())); const_cast<char *const *>(exec_args.data()));
#else // !defined(_WIN32) #elif defined(__linux__) // defined(__linux__)
auto pid = fork(); auto pid = fork();
if (pid == 0) { if (pid < 0) {
throw utils::error::create_exception(function_name, {"mount failed"});
}
if (pid != 0) {
return {};
}
setsid(); setsid();
chdir("/"); chdir("/");
close(STDIN_FILENO); close(STDIN_FILENO);
@ -685,10 +696,10 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
open("/dev/null", O_WRONLY); open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY); open("/dev/null", O_WRONLY);
execvp(exec_args.at(0U), const_cast<char *const *>(exec_args.data()));
} else {
signal(SIGCHLD, SIG_IGN); 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) #endif // defined(_WIN32)
return {}; return {};
} }

View File

@ -62,16 +62,13 @@ TEST(lock_data_test, set_and_unset_mount_state) {
json mount_state; json mount_state;
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})", EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
mount_state.dump().c_str()); mount_state["Sia1"].dump().c_str());
EXPECT_TRUE(l2.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})", EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
mount_state.dump().c_str()); mount_state["Remote1"].dump().c_str());
EXPECT_TRUE(l3.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})", EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})",
mount_state.dump().c_str()); mount_state["Remote2"].dump().c_str());
EXPECT_TRUE(l.set_mount_state(false, "C:", 99)); EXPECT_TRUE(l.set_mount_state(false, "C:", 99));
EXPECT_TRUE(l2.set_mount_state(false, "D:", 98)); EXPECT_TRUE(l2.set_mount_state(false, "D:", 98));
@ -79,15 +76,11 @@ TEST(lock_data_test, set_and_unset_mount_state) {
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state["Sia1"].dump().c_str());
EXPECT_TRUE(l2.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state["Remote1"].dump().c_str());
EXPECT_TRUE(l3.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state["Remote2"].dump().c_str());
} }
#else #else
TEST(lock_data_test, set_and_unset_mount_state) { TEST(lock_data_test, set_and_unset_mount_state) {
@ -98,13 +91,14 @@ TEST(lock_data_test, set_and_unset_mount_state) {
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})", EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})",
mount_state.dump().c_str()); mount_state["Sia1"].dump().c_str());
EXPECT_TRUE(l.set_mount_state(false, "/mnt/1", 99)); EXPECT_TRUE(l.set_mount_state(false, "/mnt/1", 99));
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(l.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state["Sia1"].dump().c_str());
} }
#endif #endif
} // namespace repertory } // namespace repertory

View File

@ -25,8 +25,8 @@
#include "utils/string.hpp" #include "utils/string.hpp"
namespace repertory::utils { namespace repertory::utils {
auto compare_version_strings(std::string version1, std::string version2) auto compare_version_strings(std::string version1,
-> std::int32_t { std::string version2) -> std::int32_t {
if (utils::string::contains(version1, "-")) { if (utils::string::contains(version1, "-")) {
version1 = utils::string::split(version1, '-', true)[0U]; version1 = utils::string::split(version1, '-', true)[0U];
@ -157,7 +157,7 @@ auto get_next_available_port(std::uint16_t first_port,
++check_port; ++check_port;
continue; continue;
} }
acceptor.set_option(boost::asio::ip::tcp::acceptor::linger(true, 0));
acceptor.bind({tcp::v4(), static_cast<std::uint16_t>(check_port)}, acceptor.bind({tcp::v4(), static_cast<std::uint16_t>(check_port)},
error_code); error_code);
if (error_code) { if (error_code) {

View File

@ -3,8 +3,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:repertory/constants.dart' as constants; import 'package:repertory/constants.dart' as constants;
import 'package:repertory/models/auth.dart'; import 'package:sodium_libs/sodium_libs.dart';
import 'package:sodium_libs/sodium_libs.dart' show SecureKey, StringX;
typedef Validator = bool Function(String); typedef Validator = bool Function(String);
@ -107,31 +106,25 @@ Map<String, dynamic> createDefaultSettings(String mountType) {
return {}; return {};
} }
void displayAuthError(Auth auth) { void displayAuthError() {
if (!auth.authenticated || constants.navigatorKey.currentContext == null) { if (constants.navigatorKey.currentContext == null) {
return; return;
} }
displayErrorMessage( displayErrorMessage(
constants.navigatorKey.currentContext!, constants.navigatorKey.currentContext!,
"Authentication failed", "Authentication failed",
clear: true,
); );
} }
void displayErrorMessage(context, String text, {bool clear = false}) { void displayErrorMessage(context, String text) {
if (!context.mounted) { if (!context.mounted) {
return; return;
} }
final messenger = ScaffoldMessenger.of(context); ScaffoldMessenger.of(
if (clear) { context,
messenger.removeCurrentSnackBar(); ).showSnackBar(SnackBar(content: Text(text, textAlign: TextAlign.center)));
}
messenger.showSnackBar(
SnackBar(content: Text(text, textAlign: TextAlign.center)),
);
} }
String formatMountName(String type, String name) { String formatMountName(String type, String name) {
@ -336,67 +329,3 @@ Map<String, dynamic> getChanged(
return changed; return changed;
} }
Future<String?> editMountLocation(
context,
List<String> available, {
bool allowEmpty = false,
String? location,
}) async {
String? currentLocation = location;
final controller = TextEditingController(text: currentLocation);
return await showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () => Navigator.of(context).pop(null),
),
TextButton(
child: const Text('OK'),
onPressed: () {
final result = getSettingValidators('Path').firstWhereOrNull(
(validator) => !validator(currentLocation ?? ''),
);
if (result != null) {
return displayErrorMessage(
context,
"Mount location is not valid",
);
}
Navigator.of(context).pop(currentLocation);
},
),
],
content:
available.isEmpty
? TextField(
autofocus: true,
controller: controller,
onChanged:
(value) => setState(() => currentLocation = value),
)
: DropdownButton<String>(
hint: const Text("Select drive"),
value: currentLocation,
onChanged:
(value) => setState(() => currentLocation = value),
items:
available.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
title: const Text('Mount Location', textAlign: TextAlign.center),
);
},
);
},
);
}

View File

@ -54,11 +54,9 @@ class Auth with ChangeNotifier {
void logoff() { void logoff() {
_authenticated = false; _authenticated = false;
_key = SecureKey.random(constants.sodium, 32);
_user = ""; _user = "";
mountList?.clear();
notifyListeners(); notifyListeners();
mountList?.clear();
} }
} }

View File

@ -101,35 +101,6 @@ class Mount with ChangeNotifier {
} }
} }
Future<void> setMountLocation(String location) async {
try {
mountConfig.path = location;
final auth = await _auth.createAuth();
final response = await http.put(
Uri.parse(
Uri.encodeFull(
'${getBaseUri()}/api/v1/mount_location?auth=$auth&name=$name&type=$type&location=$location',
),
),
);
if (response.statusCode == 401) {
_auth.logoff();
return;
}
if (response.statusCode == 404) {
_mountList?.reset();
return;
}
return refresh();
} catch (e) {
debugPrint('$e');
}
}
Future<List<String>> getAvailableLocations() async { Future<List<String>> getAvailableLocations() async {
try { try {
final auth = await _auth.createAuth(); final auth = await _auth.createAuth();
@ -207,7 +178,7 @@ class Mount with ChangeNotifier {
); );
if (response.statusCode == 401) { if (response.statusCode == 401) {
displayAuthError(_auth); displayAuthError();
_auth.logoff(); _auth.logoff();
return false; return false;
} }

View File

@ -63,7 +63,7 @@ class MountList with ChangeNotifier {
); );
if (response.statusCode == 401) { if (response.statusCode == 401) {
displayAuthError(_auth); displayAuthError();
_auth.logoff(); _auth.logoff();
return; return;
} }
@ -155,7 +155,7 @@ class MountList with ChangeNotifier {
ret = true; ret = true;
break; break;
case 401: case 401:
displayAuthError(_auth); displayAuthError();
_auth.logoff(); _auth.logoff();
break; break;
case 404: case 404:

View File

@ -38,19 +38,6 @@ class _AuthScreenState extends State<AuthScreen> {
return SizedBox.shrink(); return SizedBox.shrink();
} }
createLoginHandler() {
return _enabled
? () async {
setState(() => _enabled = false);
await auth.authenticate(
_userController.text,
_passwordController.text,
);
setState(() => _enabled = true);
}
: null;
}
return Center( return Center(
child: Card( child: Card(
child: Padding( child: Padding(
@ -72,26 +59,26 @@ class _AuthScreenState extends State<AuthScreen> {
autofocus: true, autofocus: true,
decoration: InputDecoration(labelText: 'Username'), decoration: InputDecoration(labelText: 'Username'),
controller: _userController, controller: _userController,
textInputAction: TextInputAction.next,
), ),
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
TextField( TextField(
obscureText: true, obscureText: true,
decoration: InputDecoration(labelText: 'Password'), decoration: InputDecoration(labelText: 'Password'),
controller: _passwordController, controller: _passwordController,
textInputAction: TextInputAction.go,
onSubmitted: (_) {
final handler = createLoginHandler();
if (handler == null) {
return;
}
handler();
},
), ),
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
ElevatedButton( ElevatedButton(
onPressed: createLoginHandler(), onPressed:
_enabled
? () async {
setState(() => _enabled = false);
await auth.authenticate(
_userController.text,
_passwordController.text,
);
setState(() => _enabled = true);
}
: null,
child: const Text('Login'), child: const Text('Login'),
), ),
], ],

View File

@ -17,7 +17,6 @@ class MountWidget extends StatefulWidget {
class _MountWidgetState extends State<MountWidget> { class _MountWidgetState extends State<MountWidget> {
bool _enabled = true; bool _enabled = true;
bool _editEnabled = true;
Timer? _timer; Timer? _timer;
@override @override
@ -62,33 +61,7 @@ class _MountWidgetState extends State<MountWidget> {
mount.provider, mount.provider,
style: TextStyle(color: textColor, fontWeight: FontWeight.bold), style: TextStyle(color: textColor, fontWeight: FontWeight.bold),
), ),
trailing: Row( trailing: IconButton(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (mount.mounted != null && !mount.mounted!)
IconButton(
icon: const Icon(Icons.edit),
color: subTextColor,
tooltip: 'Edit mount location',
onPressed: () async {
setState(() => _editEnabled = false);
final available = await mount.getAvailableLocations();
if (context.mounted) {
final location = await editMountLocation(
context,
available,
location: mount.path,
);
if (location != null) {
await mount.setMountLocation(location);
}
}
setState(() => _editEnabled = true);
},
),
IconButton(
icon: Icon( icon: Icon(
mount.mounted == null mount.mounted == null
? Icons.hourglass_top ? Icons.hourglass_top
@ -100,16 +73,8 @@ class _MountWidgetState extends State<MountWidget> {
? Color.fromARGB(255, 163, 96, 76) ? Color.fromARGB(255, 163, 96, 76)
: subTextColor, : subTextColor,
), ),
tooltip:
mount.mounted == null
? ''
: mount.mounted!
? 'Unmount'
: 'Mount',
onPressed: _createMountHandler(context, mount), onPressed: _createMountHandler(context, mount),
), ),
],
),
); );
}, },
), ),
@ -184,7 +149,70 @@ class _MountWidgetState extends State<MountWidget> {
return location; return location;
} }
return editMountLocation(context, await mount.getAvailableLocations()); final available = await mount.getAvailableLocations();
String? currentLocation;
return await showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () => Navigator.of(context).pop(null),
),
TextButton(
child: const Text('OK'),
onPressed: () {
final result = getSettingValidators(
'Path',
).firstWhereOrNull(
(validator) => !validator(currentLocation ?? ''),
);
if (result != null) {
return displayErrorMessage(
context,
"Mount location is not valid",
);
}
Navigator.of(context).pop(currentLocation);
},
),
],
content:
available.isEmpty
? TextField(
autofocus: true,
controller: TextEditingController(
text: currentLocation,
),
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r'\s')),
],
onChanged:
(value) => setState(() => currentLocation = value),
)
: DropdownButton<String>(
hint: const Text("Select drive"),
value: currentLocation,
onChanged:
(value) => setState(() => currentLocation = value),
items:
available.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
title: const Text('Set Mount Location'),
);
},
);
},
);
} }
@override @override

View File

@ -106,6 +106,7 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
void dispose() { void dispose() {
final settings = getChanged(widget.origSettings, widget.settings); final settings = getChanged(widget.origSettings, widget.settings);
if (settings.isNotEmpty) { if (settings.isNotEmpty) {
debugPrint("start");
final key = final key =
Provider.of<Auth>( Provider.of<Auth>(
constants.navigatorKey.currentContext!, constants.navigatorKey.currentContext!,
@ -113,13 +114,15 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
).key; ).key;
convertAllToString(settings, key) convertAllToString(settings, key)
.then((map) async { .then((map) async {
debugPrint("map");
try { try {
final authProvider = Provider.of<Auth>( final authProvider = Provider.of<Auth>(
constants.navigatorKey.currentContext!, constants.navigatorKey.currentContext!,
listen: false, listen: false,
); );
final auth = await authProvider.createAuth(); final auth = await authProvider.createAuth();
debugPrint("auth");
final response = await http.put( final response = await http.put(
Uri.parse( Uri.parse(
Uri.encodeFull( Uri.encodeFull(
@ -129,7 +132,7 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
); );
if (response.statusCode == 401) { if (response.statusCode == 401) {
displayAuthError(authProvider); displayAuthError();
authProvider.logoff(); authProvider.logoff();
} }
} catch (e) { } catch (e) {