Compare commits
640 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f41024be2 | |||
| 2420814862 | |||
| 41855bccd2 | |||
| dfa30c8a65 | |||
| 289034d0c8 | |||
| 2a23f4d14c | |||
| b20d355527 | |||
| fa9620eac1 | |||
| a201b62c04 | |||
| 9bf164ab77 | |||
| 97c6f3490c | |||
| f5cb856d3d | |||
| 67de1f6c02 | |||
| 1002265000 | |||
| 37a2dff0dd | |||
| 799cd6c394 | |||
| 2a005a2222 | |||
| 3942d5c442 | |||
| a520d52ad3 | |||
| aa1de921ee | |||
| bfeacbbdfe | |||
| afce9f159a | |||
| fd828d5b50 | |||
| b33e27e446 | |||
| c97a56eb42 | |||
| 5e5519ece7 | |||
| 6d89b7e510 | |||
| 6baddc10e9 | |||
| a2ea0060cb | |||
| 6aaab30439 | |||
| ea5d108a10 | |||
| f0b127789f | |||
| 4c8be2bfd1 | |||
| 7e371adeb0 | |||
| 6d7b491adb | |||
| abec952e39 | |||
| 9ff541059d | |||
| b84d1f20b5 | |||
| ecc806dc07 | |||
| 45c94cfcbf | |||
| f6f972c2d6 | |||
| e1dc26f676 | |||
| 2fec40c5a6 | |||
| 8f572e1259 | |||
| 43aa15d45c | |||
| 59a5540248 | |||
| 5bfb3e0f68 | |||
| c04f6cbf80 | |||
| d2863fa95b | |||
| 821fd2cf2d | |||
| 6f84ad42fc | |||
| 1d1157a902 | |||
| 6ca9e34c7e | |||
| bc29c468d8 | |||
| 925ea1a414 | |||
| c9addd407e | |||
| 242ae09857 | |||
| ba28c52912 | |||
| a98c95e695 | |||
| 26615ccad0 | |||
| 0b0972b48d | |||
| a435ead916 | |||
| 545a6b154a | |||
| 442f940cc4 | |||
| 7d9525084d | |||
| 72c972a2a7 | |||
| b72e1ef7a4 | |||
| 786ad0ff82 | |||
| 00224bdd2f | |||
| 371e069e20 | |||
| 91bb7c7dd4 | |||
| 4e901164fb | |||
| 244f20b5f4 | |||
| e4c8d42cc8 | |||
| ed99e020df | |||
| 8363de2eed | |||
| 1ae051f066 | |||
| 45848d175e | |||
| 661dcf86a8 | |||
| a7b9bbc78a | |||
| 75842fbfb6 | |||
| 4ef2ab2587 | |||
| 935ff48b15 | |||
| 51390e115f | |||
| 7bfc441d1d | |||
| 2b70cea4e0 | |||
| e4fbc7cd50 | |||
| 706f2aac83 | |||
| 73c27f0984 | |||
| 12837d44fe | |||
| 12d87d6256 | |||
| 1bfdd73e4c | |||
| 60abc03091 | |||
| e32954ea7d | |||
| 72ae7a2884 | |||
| 9f5a3b3976 | |||
| de438b93d5 | |||
| 9f8890020f | |||
| 0e023534a7 | |||
| a199393e29 | |||
| ed90ae2775 | |||
| ca8ad37adf | |||
| 4184705b14 | |||
| d976ac87e3 | |||
| c3005813a2 | |||
| 9e929ddf98 | |||
| 681efe95af | |||
| c7fab132ab | |||
| 4f8ea3e4ff | |||
| f264fd0971 | |||
| 8a354f0674 | |||
| 3f78b6c94e | |||
| 2056871dc9 | |||
| 40e66def1e | |||
| 3e5c3678d5 | |||
| 490afd0bb7 | |||
| 3fbe514f65 | |||
| 185f37f135 | |||
| 6cc7e10fcd | |||
| 85edc4cca2 | |||
| 83a9b22d82 | |||
| fca85633c1 | |||
| 83a0ef2240 | |||
| 925f3cb4f0 | |||
| 2b61e9a371 | |||
| 4f789a2e5b | |||
| d1c09784a4 | |||
| 37fa9537d0 | |||
| f97a968e0d | |||
| 59b77b5a5e | |||
| ad2c0f982a | |||
| 04d5ad0519 | |||
| f08e1b40c3 | |||
| dc982df96b | |||
| d99c648259 | |||
| 233324d6fb | |||
| 15806a3c9f | |||
| c5f8b44537 | |||
| ea90a7f086 | |||
| be793ce0f7 | |||
| 093bb60eb2 | |||
| ddaba898ee | |||
| 0816d2ab3e | |||
| 4b009f1aca | |||
| 79a23ae1c6 | |||
| 0719ffe0fa | |||
| 21eec081ee | |||
| 4fbc730490 | |||
| a89c657fe1 | |||
| 831b20d737 | |||
| 59c3b9b76e | |||
| bee3b0c588 | |||
| 1165d86c2c | |||
| ce27f76856 | |||
| 8e3ae01a30 | |||
| 6dad3aeab7 | |||
| 1d141c117b | |||
| 1dba112bce | |||
| 9d2aded70f | |||
| 82b123a345 | |||
| a34f10d9b9 | |||
| 9bf36c8737 | |||
| bea26a9a6d | |||
| 6df2ef1740 | |||
| 56e1ba64ed | |||
| 898516b33d | |||
| 2cc3f1329c | |||
| b551788c4c | |||
| ccdbbcf01c | |||
| b033496710 | |||
| a101ff07c4 | |||
| 086664adfd | |||
| ce0e21be9a | |||
| dad2161754 | |||
| a8ee27eea9 | |||
| 1bbdf46a0e | |||
| fc74738643 | |||
| 60d22ea280 | |||
| 3df6818ffe | |||
| a0bc4bbc4c | |||
| 90277653cb | |||
| ac355918ed | |||
| d6830c3428 | |||
| cbf480bef9 | |||
| afaaaf2eb5 | |||
| 14f0833d17 | |||
| 7f7d8c8f45 | |||
| 52a7de0a8b | |||
| dbded55113 | |||
| aa3105aecd | |||
| 0c48d42c2d | |||
| 836ba33fe4 | |||
| 1f41e52a86 | |||
| 17e4734cfb | |||
| b681476373 | |||
| 3a9eb82fdf | |||
| 7e5e7b032b | |||
| df291c3e9a | |||
| bf691a7f5c | |||
| 597a2264e8 | |||
| 1a2d3c8207 | |||
| 471e85d7c6 | |||
| f9f437dd85 | |||
| 143846c48a | |||
| 2fb909f73d | |||
| 0f773a9779 | |||
| 92a98a5984 | |||
| 464af37afb | |||
| f8dc3fef55 | |||
| 6d35e2697d | |||
| 0d02be2392 | |||
| 48f796ae4b | |||
| 125fe9e6e2 | |||
| 29f3094ad4 | |||
| 733aee3977 | |||
| e3ed60feae | |||
| bfa2c0a641 | |||
| 952df560ec | |||
| 1db2229ad3 | |||
| 731499be81 | |||
| 5aa0899bcf | |||
| 30b065dde3 | |||
| 1f510bee57 | |||
| 07b34c874d | |||
| 19ff73cc30 | |||
| e8633ceca2 | |||
| 770294d559 | |||
| c0c55d35f4 | |||
| a47debda91 | |||
| c449b45009 | |||
| f1f19c8263 | |||
| 31ad8505b7 | |||
| feedcfc5c4 | |||
| a5facbd648 | |||
| 3b1d04251c | |||
| fd4ec3fd1c | |||
| 0a270b267c | |||
| ec6d38d7af | |||
| a9d8ddc0f5 | |||
| dada379354 | |||
| 86c7aa33ce | |||
| 26abb75e94 | |||
| 8eec8b19dd | |||
| 582acfb385 | |||
| 456902c784 | |||
| 87215c4a1e | |||
| d361e01eef | |||
| 8e109dd562 | |||
| 8f429e1e56 | |||
| e362f732e7 | |||
| 86c2d00726 | |||
| d4b89b5dc5 | |||
| ff1255f948 | |||
| 96742cc918 | |||
| c2bb835b0f | |||
| f384492ed2 | |||
| 22a7cfa544 | |||
| 228d109692 | |||
| dc89ff95d8 | |||
| 04c56fd985 | |||
| ca03be7f43 | |||
| 35a2d98790 | |||
| c4d8b24295 | |||
| 42349ad4e1 | |||
| e7566a6316 | |||
| fdffd9a32a | |||
| 4c3413ae63 | |||
| 30e3e7289a | |||
| 12ba416ca5 | |||
| e4aeaee6e7 | |||
| 9d943bc5dc | |||
| 66d1413eb5 | |||
| e572b61af9 | |||
| f9b774ddb5 | |||
| 106b3f66a4 | |||
| 657fd50702 | |||
| 7400fae11b | |||
| 043ef6dcd2 | |||
| 6ea50bffc8 | |||
| 9584d69e97 | |||
| 5ceff44993 | |||
| 372a9bdd97 | |||
| fe82c78571 | |||
| fd11d41dc5 | |||
| 9242a810b0 | |||
| 0b9d0a4ad9 | |||
| 17d3e0ef54 | |||
| 4fd2b159a6 | |||
| d6d457f999 | |||
| 54b3bc3a7e | |||
| 2cbee3d495 | |||
| 7263ec53ac | |||
| 0edfdbdfce | |||
| 114d235a6a | |||
| a47615a274 | |||
| 1987c578bc | |||
| b2327b21fe | |||
| b22681c555 | |||
| 931913b836 | |||
| 2be0e2f88c | |||
| 7df175b747 | |||
| b6d05a6d09 | |||
| 8d88728c99 | |||
| 7141735664 | |||
| 82baeb909a | |||
| 2a85322f64 | |||
| 088cb68604 | |||
| 81be7ccf58 | |||
| a9d1a442f6 | |||
| 97043d6366 | |||
| 756bf8f93f | |||
| 9dc35c80c5 | |||
| 0f35d4a445 | |||
| 57f50b0306 | |||
| 065951d91f | |||
| 36e6c92938 | |||
| e7109e58bb | |||
| 17372a9c06 | |||
| d7297707d7 | |||
| c809c8fcbf | |||
| 68f5f88347 | |||
| 6f8e28d711 | |||
| 47dc364d4e | |||
| 3b4f940983 | |||
| 1048653eef | |||
| f1c932b7d7 | |||
| 7c420ba7d7 | |||
| 7974361e5b | |||
| 01171d788c | |||
| eec709a6e9 | |||
| 5384759261 | |||
| e3022bc52b | |||
| c7af02c15e | |||
| 96a1199fff | |||
| 2cfc2cee7c | |||
| f5e52321b8 | |||
| 58d558159d | |||
| e4e1c66bca | |||
| 1996b97e99 | |||
| cb4937b590 | |||
| 57042892c4 | |||
| 1f309606c9 | |||
| f9e6702d40 | |||
| 690aa042df | |||
| 87f01a9984 | |||
| c1272dc7a7 | |||
| 257cdacad4 | |||
| 2c9fdebf25 | |||
| 2027c69c0e | |||
| e08a13f2c3 | |||
| 6ed7e76b17 | |||
| 573bf87cbb | |||
| e8afa274a7 | |||
| d4d1eaf2e2 | |||
| 6918e78cf9 | |||
| cd93ba2197 | |||
| c052511ff3 | |||
| 19d1a9dd0e | |||
| 66400f4875 | |||
| 88a5dc2a58 | |||
| cf0af15854 | |||
| 61764e81b8 | |||
| 98c8258127 | |||
| 3527f678e5 | |||
| fce084f95a | |||
| 1ad1ebb3fd | |||
| 1de9ebdfa5 | |||
| bd1cb67cd0 | |||
| 3225f536ce | |||
| 58f91f4ac4 | |||
| 8fc37936dc | |||
| 0ca9a3b2f7 | |||
| f8ae5b70c0 | |||
| 7a3b30b012 | |||
| 4e8cffd778 | |||
| bf7eb5b986 | |||
| 77d06df7d3 | |||
| 2755aa472b | |||
| 2b7bc6965d | |||
| 08164cae68 | |||
| 2ca96c34f7 | |||
| 4b4e067fac | |||
| d2b485456a | |||
| 793b57a163 | |||
| d9218dea59 | |||
| 789361ea73 | |||
| ffc1537d7f | |||
| 9d07070ef6 | |||
| 0f0874c10a | |||
| 137241fdbb | |||
| 9515a21dc6 | |||
| c3ee948682 | |||
| 9608cc9742 | |||
| 88aaa2a71b | |||
| 133503582a | |||
| 1eede0f035 | |||
| b6f52f660a | |||
| a1f65b7e59 | |||
| 1ce707788d | |||
| 31aaec74af | |||
| 97792f0707 | |||
| b6189d12e7 | |||
| 0877638fc4 | |||
| 090d2e6586 | |||
| a64a2479ba | |||
| 856babbc21 | |||
| aad5c23f45 | |||
| d8d5b938f0 | |||
| 3afd36a821 | |||
| ce8887528f | |||
| d4dd259b5f | |||
| 256fbd6afa | |||
| 9344d94e4c | |||
| 1286248207 | |||
| 2ef8f2f93c | |||
| 86053815f0 | |||
| 88453f1ec4 | |||
| 623ab7d786 | |||
| 1ed15902a3 | |||
| 3a55075532 | |||
| 6c7866ad14 | |||
| f78b29b7fd | |||
| d8e964e06b | |||
| 5d8f9d3813 | |||
| 28bf070ce2 | |||
| 866c4d354e | |||
| e7837af0e7 | |||
| 97b9b6dc11 | |||
| 09fd01d9b5 | |||
| 8fbe02a1b7 | |||
| dce116b66f | |||
| d167780562 | |||
| 6e057afb6d | |||
| 1967de72c8 | |||
| bfcc7519bf | |||
| 1a2aca9999 | |||
| 282e5bc2a6 | |||
| f369462e7f | |||
| 6317ad2657 | |||
| 42ebbf2922 | |||
| 7c1dec9401 | |||
| ecd26a42a8 | |||
| cf0aa948fe | |||
| 934c176974 | |||
| eea8f82bf9 | |||
| 892f64fe3a | |||
| 6bc3173d3a | |||
| 93a58c0d04 | |||
| 3298b5f3ee | |||
| 6d03266ade | |||
| 1b3cf498f4 | |||
| 0bfe9803ac | |||
| 617278281e | |||
| 994db6c26a | |||
| 9f456cec9d | |||
| a95be39c17 | |||
| 36776cc61e | |||
| a309651fe4 | |||
| 716eac79bf | |||
| f3ed0f6430 | |||
| c6bb21d9ee | |||
| c7eb11a836 | |||
| 39d7ce7d1b | |||
| 02d6346b01 | |||
| a8f21068fc | |||
| 2cfe75a551 | |||
| 5a1f8411de | |||
| a86045c307 | |||
| db103de24d | |||
| e505e58192 | |||
| b75ebd13b7 | |||
| 937954fa02 | |||
| 43e32b44a2 | |||
| 61d88c3091 | |||
| 12e312593e | |||
| 4f9b94921a | |||
| 0a5a649cfe | |||
| 2f52a16d52 | |||
| 38fc6b81df | |||
| 28baabc122 | |||
| 9b12d4576c | |||
| e5cf13fd93 | |||
| dbb315a8d7 | |||
| 2c19e13c09 | |||
| fe9373af78 | |||
| eadcaa1b05 | |||
| 5f29c28e04 | |||
| db916bf970 | |||
| 2309407ca6 | |||
| 5c78b20504 | |||
| cada630e81 | |||
| a361c38e82 | |||
| 0f10626b1b | |||
| 8945196633 | |||
| 31e8359ec0 | |||
| 089c3e03f6 | |||
| fd4a7d1981 | |||
| b50e27f4b0 | |||
| 54c5d55da7 | |||
| 25fc957092 | |||
| 5393db3236 | |||
| 6df560ca37 | |||
| 789d3ba2f2 | |||
| c4391d3f4d | |||
| 1f4d07568f | |||
| e408ac5df8 | |||
| 305de9f45a | |||
| ecc819eebc | |||
| 9cbdcbcdf6 | |||
| 9c2249ed08 | |||
| 7b3b919581 | |||
| ef8f3ae6fd | |||
| 7a88f4c52d | |||
| 6e6f09dba0 | |||
| 62ea9a116b | |||
| f7d767d73e | |||
| 26a31f6d56 | |||
| 5358f1f52c | |||
| bc0c47c487 | |||
| 8463e35a10 | |||
| 2859005ad4 | |||
| 44c60280ef | |||
| 9011cf5c83 | |||
| 2c21a235b2 | |||
| 87230fb0e7 | |||
| 4488ae269b | |||
| bfafe02d48 | |||
| d7160abc47 | |||
| 76bceb121f | |||
| 952ece8c83 | |||
| 33d37700e1 | |||
| a815542604 | |||
| e796b465d1 | |||
| b511b40d7c | |||
| 599da0e348 | |||
| c7b675f841 | |||
| be6c30dfee | |||
| 7d72a86876 | |||
| adb4b7381e | |||
| f2e018277b | |||
| 9c4b6f35bb | |||
| b9ae26cb27 | |||
| ce0a9ab538 | |||
| 8999303f0a | |||
| 69e4c2592b | |||
| 1d5e1bda85 | |||
| 39fe9d55fd | |||
| a8a152e578 | |||
| 74fa671b69 | |||
| 7b0756c6bd | |||
| 17971e0a5a | |||
| d3d93b03de | |||
| 22366221b2 | |||
| b0ef675e7e | |||
| cd0d2bfdea | |||
| 4273a20180 | |||
| c4a419f2e4 | |||
| 3a51c9cc9c | |||
| c3914cc1ed | |||
| 69f341b329 | |||
| b03d790247 | |||
| df41dd1c6f | |||
| ad25d3e462 | |||
| 8097dd6721 | |||
| 803752e745 | |||
| 6328c24537 | |||
| f40f8c87f4 | |||
| 886beb62ea | |||
| 0f80fcd830 | |||
| 52315d09da | |||
| 62a111b0fc | |||
| 7194d35cf5 | |||
| 03b0b0c38b | |||
| b744671e57 | |||
| a8f58eba7c | |||
| 46d35adfdb | |||
| eb66c5c07a | |||
| 13bfad4966 | |||
| e47944a3b0 | |||
| 9cb2d6d02f | |||
| 36568c3845 | |||
| a208fbedbd | |||
| 584bcf1cdf | |||
| c5d00b4766 | |||
| 5fe2036f0b | |||
| 583301ff40 | |||
| 88c95d6bf7 | |||
| 848a611751 | |||
| 2e44ef3501 | |||
| ecb381e1ca | |||
| b2ab827a55 | |||
| 46551f9330 | |||
| e4339c34ec | |||
| 7d2a1c6952 | |||
| 24a0d118f2 | |||
| ef13b68592 | |||
| c4a3e4e9a7 | |||
| 7f4db0c1dc | |||
| 3dd6062af4 | |||
| 6e2dc89670 | |||
| 93bc143dac | |||
| 03a37f72aa | |||
| 7e176262cc | |||
| e1c1d7ea39 | |||
| c355f6fdbb | |||
| 0a715fef08 | |||
| 395219d0cb | |||
| eb3f9f91ca | |||
| cf578bd005 | |||
| 1f575b2bc0 | |||
| 91b7e1071c | |||
| 329c74d3ce | |||
| 8674ac7211 | |||
| 328b9ba46c | |||
| 9b7ee4e746 | |||
| 27070b6a4c | |||
| 46c8f0c48a | |||
| a7c33e7d43 | |||
| 718f5ba514 | |||
| f3672f81dd | |||
| 6b5e48d6fe | |||
| 486bce8bc7 | |||
| 435551c207 | |||
| de388a937a | |||
| 4fa771a0b5 | |||
| 6ba5cdddc5 | |||
| d4e2dc8cb6 | |||
| 425934d3ef | |||
| c4d81135a2 | |||
| 2b4382d013 | |||
| 7523e536b5 | |||
| 909432a6ce | |||
| ceacd8469d | |||
| 7869043c28 | |||
| bfafd2a044 | |||
| 1bd19ad917 | |||
| e52f072091 | |||
| 22ac7f6b15 | |||
| 196f87c219 | |||
| e1a789fa36 |
+41
-4
@@ -1,6 +1,43 @@
|
|||||||
/sites
|
Dockerfile
|
||||||
/cli
|
.github
|
||||||
/desktop
|
.vscode
|
||||||
/backend # go backend
|
*.md
|
||||||
|
|
||||||
|
#### gitignore below
|
||||||
|
# Nuxt dev/build outputs
|
||||||
|
.output
|
||||||
|
.data
|
||||||
|
.nuxt
|
||||||
|
.nitro
|
||||||
|
.cache
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Node dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
.yarn
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
.fleet
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Local env files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
|
||||||
|
# deploy template
|
||||||
|
deploy-template/*
|
||||||
|
|
||||||
|
!deploy-template/compose.yml
|
||||||
|
|
||||||
|
# generated prisma client
|
||||||
|
/prisma/client
|
||||||
|
/prisma/validate
|
||||||
|
|||||||
@@ -3,5 +3,3 @@ DATABASE_URL="postgres://drop:drop@127.0.0.1:5432/drop"
|
|||||||
GIANT_BOMB_API_KEY=""
|
GIANT_BOMB_API_KEY=""
|
||||||
|
|
||||||
EXTERNAL_URL="http://localhost:3000"
|
EXTERNAL_URL="http://localhost:3000"
|
||||||
|
|
||||||
NUXT_PORT=4000
|
|
||||||
@@ -1,24 +1,12 @@
|
|||||||
name: Server CI
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [develop]
|
branches:
|
||||||
paths:
|
- develop
|
||||||
- "server/**"
|
|
||||||
- "libraries/base/**"
|
|
||||||
- "package.json"
|
|
||||||
- "pnpm-lock.yaml"
|
|
||||||
- "pnpm-workspace.yaml"
|
|
||||||
- ".github/workflows/server-ci.yml"
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [develop]
|
branches:
|
||||||
paths:
|
- develop
|
||||||
- "server/**"
|
|
||||||
- "libraries/base/**"
|
|
||||||
- "package.json"
|
|
||||||
- "pnpm-lock.yaml"
|
|
||||||
- "pnpm-workspace.yaml"
|
|
||||||
- ".github/workflows/server-ci.yml"
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -30,6 +18,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
@@ -44,7 +34,6 @@ jobs:
|
|||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
working-directory: server
|
|
||||||
run: pnpm run typecheck
|
run: pnpm run typecheck
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -53,6 +42,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
@@ -67,5 +58,4 @@ jobs:
|
|||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
working-directory: server
|
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
name: "Build and release desktop"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tagName:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: "tagName to be associated with this release."
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
# This can be used to automatically publish nightlies at UTC nighttime
|
|
||||||
# schedule:
|
|
||||||
# - cron: "0 2 * * *" # run at 2 AM UTC
|
|
||||||
|
|
||||||
# This workflow will trigger on each push to the `release` branch to create or update a GitHub release, build your app, and upload the artifacts to the release.
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-tauri:
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: "macos-14" # for Arm based macs (M1 and above).
|
|
||||||
args: "--target aarch64-apple-darwin"
|
|
||||||
- platform: "macos-14" # for Intel based macs.
|
|
||||||
args: "--target x86_64-apple-darwin"
|
|
||||||
- platform: "ubuntu-22.04" # for Tauri v1 you could replace this with ubuntu-20.04.
|
|
||||||
args: ""
|
|
||||||
- platform: "ubuntu-22.04-arm"
|
|
||||||
args: "--target aarch64-unknown-linux-gnu"
|
|
||||||
- platform: "windows-latest"
|
|
||||||
args: ""
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: setup pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: setup node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: lts/*
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
|
|
||||||
- name: install Rust nightly
|
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
|
|
||||||
targets: ${{ matrix.platform == 'macos-14' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
|
||||||
|
|
||||||
- name: Rust cache
|
|
||||||
uses: swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: './desktop/src-tauri -> target'
|
|
||||||
|
|
||||||
- name: install dependencies (ubuntu only)
|
|
||||||
if: matrix.platform == 'ubuntu-22.04' || matrix.platform == 'ubuntu-22.04-arm' # This must match the platform value defined above.
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils
|
|
||||||
# webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2.
|
|
||||||
|
|
||||||
- name: Import Apple Developer Certificate
|
|
||||||
if: matrix.platform == 'macos-14'
|
|
||||||
env:
|
|
||||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
|
||||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
||||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
|
|
||||||
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
|
||||||
security default-keychain -s build.keychain
|
|
||||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
|
||||||
security set-keychain-settings -t 3600 -u build.keychain
|
|
||||||
|
|
||||||
# Add build.keychain to the user keychain search list so that codesign
|
|
||||||
# (invoked later by tauri-action WITHOUT an explicit --keychain) can
|
|
||||||
# resolve the signing identity from it.
|
|
||||||
security list-keychains -d user -s build.keychain $(security list-keychains -d user | tr -d '"')
|
|
||||||
|
|
||||||
echo "Created keychain"
|
|
||||||
|
|
||||||
curl https://droposs.org/drop.der --output drop.der
|
|
||||||
|
|
||||||
# swiftc libs/appletrust/add-certificate.swift
|
|
||||||
# ./add-certificate drop.der
|
|
||||||
# rm add-certificate
|
|
||||||
|
|
||||||
# echo "Added certificate to keychain using swift util"
|
|
||||||
|
|
||||||
## Script is equivalent to:
|
|
||||||
sudo security authorizationdb write com.apple.trust-settings.admin allow
|
|
||||||
sudo security add-trusted-cert -d -r trustRoot -k build.keychain -p codeSign -u -1 drop.der
|
|
||||||
sudo security authorizationdb remove com.apple.trust-settings.admin
|
|
||||||
|
|
||||||
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
|
||||||
echo "Imported certificate"
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
|
|
||||||
security find-identity -v -p codesigning build.keychain
|
|
||||||
|
|
||||||
- name: Verify Certificate
|
|
||||||
if: matrix.platform == 'macos-14'
|
|
||||||
run: |
|
|
||||||
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Drop OSS")
|
|
||||||
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
|
|
||||||
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
|
|
||||||
echo "Certificate imported. Using identity: $CERT_ID"
|
|
||||||
|
|
||||||
- name: install frontend dependencies
|
|
||||||
run: pnpm install # change this to npm, pnpm or bun depending on which one you use.
|
|
||||||
|
|
||||||
- uses: tauri-apps/tauri-action@v0
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# Do NOT set APPLE_CERTIFICATE / APPLE_CERTIFICATE_PASSWORD here. Doing so
|
|
||||||
# makes tauri-action import the cert into its own throwaway keychain and
|
|
||||||
# look up the identity by Apple-only name prefixes (e.g.
|
|
||||||
# "Developer ID Application:"), which never matches our "Drop OSS" cert
|
|
||||||
# and fails with "failed to resolve signing identity". Instead we rely on
|
|
||||||
# the build.keychain prepared above and only pass the resolved identity.
|
|
||||||
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
|
|
||||||
NO_STRIP: true
|
|
||||||
with:
|
|
||||||
tagName: ${{ inputs.print_tags || 'v__VERSION__' }} # the action automatically replaces \_\_VERSION\_\_ with the app version.
|
|
||||||
releaseName: "Auto-release v__VERSION__"
|
|
||||||
releaseBody: "See the assets to download this version and install. This release was created automatically."
|
|
||||||
releaseDraft: false
|
|
||||||
prerelease: true
|
|
||||||
args: ${{ matrix.args }}
|
|
||||||
projectPath: './desktop'
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
name: Droplet CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [develop]
|
|
||||||
paths:
|
|
||||||
- "libraries/droplet/**"
|
|
||||||
- "libraries/droplet_types/**"
|
|
||||||
- "libraries/libarchive/**"
|
|
||||||
- ".github/workflows/droplet-ci.yml"
|
|
||||||
pull_request:
|
|
||||||
branches: [develop]
|
|
||||||
paths:
|
|
||||||
- "libraries/droplet/**"
|
|
||||||
- "libraries/droplet_types/**"
|
|
||||||
- "libraries/libarchive/**"
|
|
||||||
- ".github/workflows/droplet-ci.yml"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
name: Build, Test, Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: libraries/droplet
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
- name: Rust cache
|
|
||||||
uses: swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: "./libraries/droplet -> target"
|
|
||||||
|
|
||||||
- name: Install libarchive
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libarchive-dev
|
|
||||||
|
|
||||||
- name: Check formatting
|
|
||||||
run: cargo fmt --all -- --check
|
|
||||||
|
|
||||||
- name: Run Clippy (lint)
|
|
||||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --all-features --all --verbose
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
name: Deploy website to GitHub Pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Runs on pushes targeting the default branch
|
|
||||||
push:
|
|
||||||
branches: [develop]
|
|
||||||
paths:
|
|
||||||
- "sites/promo/**"
|
|
||||||
- "sites/docs/**"
|
|
||||||
- "package.json"
|
|
||||||
- "pnpm-lock.yaml"
|
|
||||||
- "pnpm-workspace.yaml"
|
|
||||||
- ".github/workflows/pages.yml"
|
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
# Allow only one concurrent deployment per the "pages" group, skipping runs queued
|
|
||||||
# between the in-progress run and the latest queued one. cancel-in-progress defaults
|
|
||||||
# to false, so in-flight production deployments are allowed to complete.
|
|
||||||
concurrency: "pages"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
cache: "pnpm"
|
|
||||||
|
|
||||||
# Only install the promo site (radiant) and docs site (docs-next) and their
|
|
||||||
# dependencies so the public website deploy stays decoupled from the
|
|
||||||
# server/desktop build pipelines.
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --filter radiant... --filter docs-next...
|
|
||||||
|
|
||||||
- name: Setup Pages
|
|
||||||
id: setup_pages
|
|
||||||
uses: actions/configure-pages@v5
|
|
||||||
|
|
||||||
- name: Restore cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
sites/promo/.next/cache
|
|
||||||
# Generate a new cache whenever packages or source files change.
|
|
||||||
key: ${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('sites/promo/**.[jt]s', 'sites/promo/**.[jt]sx') }}
|
|
||||||
# If source files changed but packages didn't, rebuild from a prior cache.
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-
|
|
||||||
|
|
||||||
- name: Build promo site with Next.js
|
|
||||||
working-directory: sites/promo
|
|
||||||
run: pnpm run build
|
|
||||||
env:
|
|
||||||
PAGES_BASE_PATH: ${{ steps.setup_pages.outputs.base_path }}
|
|
||||||
|
|
||||||
- name: Build docs site with Astro
|
|
||||||
working-directory: sites/docs
|
|
||||||
run: pnpm run build
|
|
||||||
|
|
||||||
# Nest the Starlight docs (built with base: "/docs") inside the promo export
|
|
||||||
# so both ship from a single GitHub Pages deployment at /docs.
|
|
||||||
- name: Assemble docs into /docs
|
|
||||||
run: |
|
|
||||||
rm -rf sites/promo/out/docs
|
|
||||||
mkdir -p sites/promo/out/docs
|
|
||||||
cp -r sites/docs/dist/. sites/promo/out/docs/
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: sites/promo/out
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
steps:
|
|
||||||
- name: Deploy to GitHub Pages
|
|
||||||
id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Build and release server
|
name: Release Workflow
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
@@ -8,20 +8,10 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: "0 2 * * *" # run at 2 AM UTC
|
- cron: "0 2 * * *" # run at 2 AM UTC
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY_IMAGE: ghcr.io/drop-oss/drop
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
web:
|
||||||
strategy:
|
name: Push website Docker image to registry
|
||||||
fail-fast: false
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
permissions:
|
||||||
packages: write
|
packages: write
|
||||||
contents: read
|
contents: read
|
||||||
@@ -29,35 +19,12 @@ jobs:
|
|||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
submodules: true
|
||||||
fetch-depth: 3 # fix for when this gets triggered by tag
|
fetch-depth: 3 # fix for when this gets triggered by tag
|
||||||
fetch-tags: true
|
fetch-tags: true
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Determine final version
|
- name: Determine final version
|
||||||
id: get_final_ver
|
id: get_final_ver
|
||||||
run: |
|
run: |
|
||||||
@@ -76,58 +43,22 @@ jobs:
|
|||||||
echo "Drop's release tag will be: $FINAL_VER"
|
echo "Drop's release tag will be: $FINAL_VER"
|
||||||
echo "final_ver=$FINAL_VER" >> $GITHUB_OUTPUT
|
echo "final_ver=$FINAL_VER" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Build and push by digest
|
- name: Set up QEMU
|
||||||
id: build
|
uses: docker/setup-qemu-action@v3
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
with:
|
||||||
platforms: ${{ matrix.platform }}
|
buildkitd-flags: --debug
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
tags: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
|
||||||
provenance: mode=max
|
|
||||||
sbom: true
|
|
||||||
build-args: |
|
|
||||||
BUILD_DROP_VERSION=${{ steps.get_final_ver.outputs.final_ver }}
|
|
||||||
BUILD_GIT_REF=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
- name: Log in to the Container registry
|
||||||
run: |
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
permissions:
|
|
||||||
packages: write
|
|
||||||
contents: read
|
|
||||||
steps:
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: ${{ runner.temp }}/digests
|
|
||||||
pattern: digests-*
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
@@ -146,12 +77,33 @@ jobs:
|
|||||||
# set latest tag for stable releases
|
# set latest tag for stable releases
|
||||||
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.release.prerelease == false }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.release.prerelease == false }}
|
||||||
|
|
||||||
- name: Create manifest list and push
|
- name: Cache
|
||||||
working-directory: ${{ runner.temp }}/digests
|
uses: actions/cache@v4
|
||||||
run: |
|
id: cache
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
with:
|
||||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
path: cache-mount
|
||||||
|
key: cache-mount-${{ hashFiles('Dockerfile') }}
|
||||||
|
|
||||||
- name: Inspect image
|
- name: Restore Docker cache mounts
|
||||||
run: |
|
uses: reproducible-containers/buildkit-cache-dance@v3
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
with:
|
||||||
|
builder: ${{ steps.setup-buildx.outputs.name }}
|
||||||
|
cache-dir: cache-mount
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
skip-extraction: ${{ steps.cache.outputs.cache-hit }}
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
id: build-and-push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
provenance: mode=max
|
||||||
|
sbom: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
build-args: |
|
||||||
|
BUILD_DROP_VERSION=${{ steps.get_final_ver.outputs.final_ver }}
|
||||||
+37
-2
@@ -1,2 +1,37 @@
|
|||||||
dist/
|
# Nuxt dev/build outputs
|
||||||
node_modules/
|
.output
|
||||||
|
.data
|
||||||
|
.nuxt
|
||||||
|
.nitro
|
||||||
|
.cache
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Node dependencies
|
||||||
|
node_modules
|
||||||
|
.yarn
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
.fleet
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Local env files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
|
||||||
|
# deploy template
|
||||||
|
deploy-template/*
|
||||||
|
|
||||||
|
!deploy-template/compose.yml
|
||||||
|
|
||||||
|
# generated prisma client
|
||||||
|
/prisma/client
|
||||||
|
/prisma/validate
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
variables:
|
||||||
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker:24.0.5-dind
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
variables:
|
||||||
|
IMAGE_NAME: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA
|
||||||
|
LATEST_IMAGE_NAME: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest
|
||||||
|
PUBLISH_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
||||||
|
PUBLISH_LATEST_IMAGE_NAME: $CI_REGISTRY_IMAGE:latest
|
||||||
|
script:
|
||||||
|
- docker build -t $IMAGE_NAME .
|
||||||
|
- docker image tag $IMAGE_NAME $LATEST_IMAGE_NAME
|
||||||
|
- docker push $IMAGE_NAME
|
||||||
|
- docker push $LATEST_IMAGE_NAME
|
||||||
|
- |
|
||||||
|
if [ $CI_COMMIT_TAG ]; then
|
||||||
|
docker image tag $IMAGE_NAME $PUBLISH_IMAGE_NAME
|
||||||
|
docker image tag $IMAGE_NAME $PUBLISH_LATEST_IMAGE_NAME
|
||||||
|
docker push $PUBLISH_IMAGE_NAME $PUBLISH_LATEST_IMAGE_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
build-arm64:
|
||||||
|
stage: build
|
||||||
|
image: arm64v8/docker:latest
|
||||||
|
tags:
|
||||||
|
- aarch64
|
||||||
|
variables:
|
||||||
|
IMAGE_NAME: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA-arm64
|
||||||
|
LATEST_IMAGE_NAME: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest-arm64
|
||||||
|
PUBLISH_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG-arm64
|
||||||
|
PUBLISH_LATEST_IMAGE_NAME: $CI_REGISTRY_IMAGE:latest-arm64
|
||||||
|
script:
|
||||||
|
- docker build -t $IMAGE_NAME . --platform=linux/arm64
|
||||||
|
- docker image tag $IMAGE_NAME $LATEST_IMAGE_NAME
|
||||||
|
- docker push $IMAGE_NAME
|
||||||
|
- docker push $LATEST_IMAGE_NAME
|
||||||
|
- |
|
||||||
|
if [ $CI_COMMIT_TAG ]; then
|
||||||
|
docker image tag $IMAGE_NAME $PUBLISH_IMAGE_NAME
|
||||||
|
docker image tag $IMAGE_NAME $PUBLISH_LATEST_IMAGE_NAME
|
||||||
|
docker push $PUBLISH_IMAGE_NAME
|
||||||
|
docker push $PUBLISH_LATEST_IMAGE_NAME
|
||||||
|
fi
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "drop-base"]
|
||||||
|
path = drop-base
|
||||||
|
url = https://github.com/Drop-OSS/drop-base.git
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
drop-base/
|
drop-base/
|
||||||
# file is fully managed by pnpm, no reason to break it
|
# file is fully managed by pnpm, no reason to break it
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
|
||||||
/torrential/
|
|
||||||
.data/**
|
|
||||||
**/.data/**
|
|
||||||
+28
-26
@@ -1,35 +1,37 @@
|
|||||||
{
|
{
|
||||||
|
"spellchecker.ignoreWordsList": ["mTLS", "Wireguard"],
|
||||||
|
"sqltools.connections": [
|
||||||
|
{
|
||||||
|
"previewLimit": 50,
|
||||||
|
"server": "localhost",
|
||||||
|
"port": 5432,
|
||||||
|
"driver": "PostgreSQL",
|
||||||
|
"name": "drop",
|
||||||
|
"database": "drop",
|
||||||
|
"username": "drop",
|
||||||
|
"password": "drop"
|
||||||
|
}
|
||||||
|
],
|
||||||
// allow autocomplete for ArkType expressions like "string | num"
|
// allow autocomplete for ArkType expressions like "string | num"
|
||||||
"editor.quickSuggestions": {
|
"editor.quickSuggestions": {
|
||||||
"strings": "on"
|
"strings": "on"
|
||||||
},
|
},
|
||||||
"i18n-ally.extract.autoDetect": true,
|
// prioritize ArkType's "type" for autoimports
|
||||||
"i18n-ally.extract.ignored": ["string >= 14", "string.alphanumeric >= 5"],
|
"typescript.preferences.autoImportSpecifierExcludeRegexes": ["^(node:)?os$"],
|
||||||
"i18n-ally.extract.ignoredByFiles": {
|
|
||||||
"components/NewsArticleCreateButton.vue": ["[", "`", "Enter"],
|
|
||||||
"pages/admin/library/sources/index.vue": ["Filesystem"],
|
|
||||||
"server/api/v1/auth/signin/simple.post.ts": ["boolean | undefined"]
|
|
||||||
},
|
|
||||||
"i18n-ally.keepFulfilled": true,
|
|
||||||
"i18n-ally.keystyle": "nested",
|
|
||||||
"i18n-ally.localesPaths": ["i18n", "i18n/locales"],
|
|
||||||
// i18n Ally settings
|
// i18n Ally settings
|
||||||
"i18n-ally.sortKeys": true,
|
"i18n-ally.sortKeys": true,
|
||||||
"prisma.pinToPrisma6": false,
|
"i18n-ally.keepFulfilled": true,
|
||||||
"spellchecker.ignoreWordsList": ["mTLS", "Wireguard"],
|
"i18n-ally.extract.autoDetect": true,
|
||||||
"sqltools.connections": [
|
"i18n-ally.localesPaths": ["i18n", "i18n/locales"],
|
||||||
{
|
"i18n-ally.keystyle": "nested",
|
||||||
"database": "drop",
|
"i18n-ally.extract.ignored": [
|
||||||
"driver": "PostgreSQL",
|
"string >= 14",
|
||||||
"name": "drop",
|
"string.alphanumeric >= 5",
|
||||||
"password": "drop",
|
"/api/v1/admin/import/version/preload?id=${encodeURIComponent(\n gameId,\n )}&version=${encodeURIComponent(version)}"
|
||||||
"port": 5432,
|
|
||||||
"previewLimit": 50,
|
|
||||||
"server": "localhost",
|
|
||||||
"username": "drop"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"typescript.experimental.useTsgo": false,
|
"i18n-ally.extract.ignoredByFiles": {
|
||||||
// prioritize ArkType's "type" for autoimports
|
"pages/admin/library/sources/index.vue": ["Filesystem"],
|
||||||
"typescript.preferences.autoImportSpecifierExcludeRegexes": ["^(node:)?os$"]
|
"components/NewsArticleCreateButton.vue": ["[", "`", "Enter"],
|
||||||
|
"server/api/v1/auth/signin/simple.post.ts": ["boolean | undefined"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+21
-65
@@ -1,105 +1,61 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
# Pinned to bookworm so the glibc here matches the torrential build stage
|
FROM node:lts-alpine AS base
|
||||||
# and the libarchive runtime package is named `libarchive13` (trixie renames it to libarchive13t64).
|
|
||||||
FROM node:lts-bookworm-slim AS base
|
|
||||||
ENV PNPM_HOME="/pnpm"
|
ENV PNPM_HOME="/pnpm"
|
||||||
ENV PATH="$PNPM_HOME:$PATH"
|
ENV PATH="$PNPM_HOME:$PATH"
|
||||||
RUN corepack enable
|
RUN corepack enable
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
## so corepack knows pnpm's version
|
# so corepack knows pnpm's version
|
||||||
COPY . .
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
## prevent prompt to download
|
# prevent prompt to download
|
||||||
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
||||||
## setup for offline
|
# setup for offline
|
||||||
RUN corepack pack
|
RUN corepack pack
|
||||||
## don't call out to network anymore
|
# don't call out to network anymore
|
||||||
ENV COREPACK_ENABLE_NETWORK=0
|
ENV COREPACK_ENABLE_NETWORK=0
|
||||||
|
|
||||||
### INSTALL DEPS ONCE
|
### Unified deps builder
|
||||||
FROM base AS deps
|
FROM base AS deps
|
||||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||||
|
|
||||||
### BUILD TORRENTIAL
|
### Build for app
|
||||||
# Bookworm-pinned to match the runtime image's glibc (a trixie build would not run on bookworm).
|
|
||||||
FROM rustlang/rust:nightly-bookworm-slim AS torrential-build
|
|
||||||
## libarchive-dev + pkg-config let libarchive3-sys link libarchive dynamically (glibc).
|
|
||||||
## protobuf-compiler is kept for parity (torrential's build.rs uses a vendored protoc).
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
pkg-config \
|
|
||||||
libarchive-dev \
|
|
||||||
protobuf-compiler \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
WORKDIR /build
|
|
||||||
COPY . .
|
|
||||||
RUN cargo build --release --manifest-path ./torrential/Cargo.toml
|
|
||||||
|
|
||||||
### BUILD APP
|
|
||||||
FROM base AS build-system
|
FROM base AS build-system
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV NUXT_TELEMETRY_DISABLED=1
|
ENV NUXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
## add git so drop can determine its git ref at build
|
# add git so drop can determine its git ref at build
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends git \
|
RUN apk add --no-cache git
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
## copy deps and rest of project files
|
# copy deps and rest of project files
|
||||||
COPY . .
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
ARG BUILD_DROP_VERSION
|
ARG BUILD_DROP_VERSION
|
||||||
ARG BUILD_GIT_REF
|
ARG BUILD_GIT_REF
|
||||||
|
|
||||||
## build
|
# build
|
||||||
RUN pnpm run --filter=drop postinstall && pnpm run --filter=drop build
|
RUN pnpm run postinstall && pnpm run build
|
||||||
|
|
||||||
|
### create run environment for Drop
|
||||||
# create run environment for Drop
|
|
||||||
FROM base AS run-system
|
FROM base AS run-system
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV NUXT_TELEMETRY_DISABLED=1
|
ENV NUXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
# The base stage's `COPY . .` puts the whole repo into the runtime WORKDIR (/app),
|
|
||||||
# but at runtime only the artifacts copied explicitly below are needed. Drop the
|
|
||||||
# inherited `torrential` source dir: the service resolves the binary by scanning
|
|
||||||
# the cwd for `torrential`, and a directory there is spawned as ./torrential and
|
|
||||||
# fails with EACCES. With it gone, resolution falls through to the `torrential`
|
|
||||||
# binary installed on PATH (/usr/bin/torrential) below.
|
|
||||||
RUN rm -rf /app/torrential
|
|
||||||
|
|
||||||
# RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn add --network-timeout 1000000 --no-lockfile --ignore-scripts prisma@6.11.1
|
# RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn add --network-timeout 1000000 --no-lockfile --ignore-scripts prisma@6.11.1
|
||||||
## runtime deps:
|
RUN apk add --no-cache pnpm 7zip
|
||||||
## - libarchive13: torrential now links libarchive dynamically (glibc build)
|
RUN pnpm install prisma@6.11.1
|
||||||
## - p7zip-full: provides the 7z CLI
|
|
||||||
## - nginx: front-end proxy
|
|
||||||
## - openssl + ca-certificates: required by Prisma's query engine on Debian
|
|
||||||
## pnpm itself is provided by corepack (enabled in the base stage)
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
libarchive13 \
|
|
||||||
p7zip-full \
|
|
||||||
nginx \
|
|
||||||
openssl \
|
|
||||||
ca-certificates \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
RUN pnpm install prisma@7.7.0 --global
|
|
||||||
# init prisma to download all required files
|
# init prisma to download all required files
|
||||||
RUN pnpm prisma init
|
RUN pnpm prisma init
|
||||||
|
|
||||||
COPY --from=build-system /app/server/prisma.config.ts ./
|
COPY --from=build-system /app/prisma.config.ts ./
|
||||||
COPY --from=build-system /app/server/.output ./app
|
COPY --from=build-system /app/.output ./app
|
||||||
COPY --from=build-system /app/server/prisma ./prisma
|
COPY --from=build-system /app/prisma ./prisma
|
||||||
COPY --from=build-system /app/server/build ./startup
|
COPY --from=build-system /app/build ./startup
|
||||||
COPY --from=build-system /app/server/build/nginx.conf /nginx.conf
|
|
||||||
COPY --from=torrential-build /build/torrential/target/release/torrential /usr/bin/
|
|
||||||
|
|
||||||
ENV LIBRARY="/library"
|
ENV LIBRARY="/library"
|
||||||
ENV DATA="/data"
|
ENV DATA="/data"
|
||||||
ENV NGINX_CONFIG="/nginx.conf"
|
|
||||||
# Nuxt's port
|
|
||||||
ENV PORT=4000
|
|
||||||
|
|
||||||
CMD ["sh", "/app/startup/launch.sh"]
|
CMD ["sh", "/app/startup/launch.sh"]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# Drop
|
# Drop
|
||||||
|
|
||||||
[](https://droposs.org)
|
[](https://droposs.org)
|
||||||
[](https://droposs.org/docs)
|
[](https://docs.droposs.org/)
|
||||||
[](https://forum.droposs.org)
|
[](https://forum.droposs.org)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://discord.gg/ACq4qZp4a9)
|
[](https://discord.gg/ACq4qZp4a9)
|
||||||
@@ -28,7 +28,7 @@ Drop is an open-source game distribution platform, similar to GameVault or Steam
|
|||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
See our documentation on how to [deploy Drop](https://droposs.org/docs/admin/quickstart) for more information.
|
See our documentation on how to [deploy Drop](https://docs.droposs.org/docs/guides/quickstart) for more information.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
To report a vulnerability, please DO NOT create an issue for it
|
To report a vulnerability, please DO NOT create an issue for it
|
||||||
as this may lead to the vulnerability being exploited before it
|
as this may lead to the vulnerability being exploited before it
|
||||||
can be fixed. Instead, please email [security@droposs.org](mailto:security@droposs.org)
|
can be fixed. Instead, please email [security@deepcore.dev](mailto:security@deepcore.dev)
|
||||||
|
|||||||
@@ -29,11 +29,10 @@ await updateUser();
|
|||||||
|
|
||||||
const user = useUser();
|
const user = useUser();
|
||||||
const apiDetails = await $dropFetch("/api/v1");
|
const apiDetails = await $dropFetch("/api/v1");
|
||||||
const clientMode = isClientRequest();
|
|
||||||
|
|
||||||
const showExternalUrlWarning = ref(false);
|
const showExternalUrlWarning = ref(false);
|
||||||
function checkExternalUrl() {
|
function checkExternalUrl() {
|
||||||
if (!import.meta.client || clientMode) return;
|
if (!import.meta.client) return;
|
||||||
const realOrigin = window.location.origin.trim();
|
const realOrigin = window.location.origin.trim();
|
||||||
const chosenOrigin = apiDetails.external.trim();
|
const chosenOrigin = apiDetails.external.trim();
|
||||||
const ignore = window.localStorage.getItem("ignoreExternalUrl");
|
const ignore = window.localStorage.getItem("ignoreExternalUrl");
|
||||||
@@ -52,3 +51,15 @@ if (user.value?.admin) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* You can customise the default animation here. */
|
||||||
|
|
||||||
|
::view-transition-old(root) {
|
||||||
|
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1 +0,0 @@
|
|||||||
/bin
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
func connect() {
|
|
||||||
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer conn.Close(context.Background())
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
module drop/core
|
|
||||||
|
|
||||||
go 1.26.1
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
|
||||||
github.com/jackc/pgx/v5 v5.9.1 // indirect
|
|
||||||
golang.org/x/text v0.29.0 // indirect
|
|
||||||
)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
|
||||||
github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc=
|
|
||||||
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
|
||||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
module drop
|
|
||||||
|
|
||||||
go 1.26.1
|
|
||||||
|
|
||||||
require github.com/gorilla/mux v1.8.1
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
go 1.26.1
|
|
||||||
|
|
||||||
use ./core
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
|
||||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
||||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handler(res http.ResponseWriter, req *http.Request) {
|
|
||||||
fmt.Fprintf(res, "G'day there mate")
|
|
||||||
}
|
|
||||||
func routingMiddleware(h http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
url := *r.URL
|
|
||||||
url.Path = strings.TrimSuffix(r.URL.Path, "/")
|
|
||||||
r.URL = &url
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := mux.NewRouter().StrictSlash(true)
|
|
||||||
r.Use(routingMiddleware)
|
|
||||||
|
|
||||||
r.HandleFunc("/api/v1", handler)
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: ":3433",
|
|
||||||
Handler: r,
|
|
||||||
}
|
|
||||||
log.Printf("starting drop server on :3433")
|
|
||||||
srv.ListenAndServe()
|
|
||||||
}
|
|
||||||
+544
@@ -0,0 +1,544 @@
|
|||||||
|
## Release 0.2.0-beta
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- fix recursive dirs util #02d6346
|
||||||
|
- Fix username length requirement #0a5a649
|
||||||
|
- remove dynamic imports #0f10626
|
||||||
|
- fix for missing developers or publishers #25fc957
|
||||||
|
- split prisma schemas #2859005
|
||||||
|
- results are returned alphabetically #33d3770
|
||||||
|
- update prisma schemas #36776cc
|
||||||
|
- removed global flag #43e32b4
|
||||||
|
- properly disconnect websockets from task handler #5358f1f
|
||||||
|
- follow best practices #54c5d55
|
||||||
|
- future lenience #5c78b20
|
||||||
|
- fix width of token breaking things #61d88c3
|
||||||
|
- fixed websocket authentication #62ea9a1
|
||||||
|
- fix delta manifest generation #6df560c
|
||||||
|
- admin invitation w/ system user #8463e35
|
||||||
|
- properly import icons #8945196
|
||||||
|
- prisma create footprint #952ece8
|
||||||
|
- game panel now always shows 3 lines exactly #9c2249e
|
||||||
|
- remove unnecessary import #a361c38
|
||||||
|
- fix disconnect code #a8f2106
|
||||||
|
- fix types #b511b40
|
||||||
|
- add drop-base as git submodule #b75ebd1
|
||||||
|
- Update README.md with discord link #c6bb21d
|
||||||
|
- fix expires requirement in the admin endpoint #c7b675f
|
||||||
|
- fix always being created as admin #c7eb11a
|
||||||
|
- moved icons and created PlatformClient so we can use the enum on the frontend #cada630
|
||||||
|
- recurse submodules #db103de
|
||||||
|
- fix FATAL: "root"... message #dbb315a
|
||||||
|
- only show versions that are directories #ef8f3ae
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- update prisma & delete games #089c3e0
|
||||||
|
- manual handshake #12e3125
|
||||||
|
- fetch game endpoint #1f4d075
|
||||||
|
- under the hood organisation and consolidation #26a31f6
|
||||||
|
- 'no images' slide on image carousel #28baabc
|
||||||
|
- improve feedback when metadata fails #2c19e13
|
||||||
|
- introduction of 'system user' #2c21a23
|
||||||
|
- change name, description and icon #2cfe75a
|
||||||
|
- 'manual' metadata provider #2f52a16
|
||||||
|
- add disabled state #38fc6b8
|
||||||
|
- overhauled version importing #39d7ce7
|
||||||
|
- automatically create library folder if it doesn't exist #39fe9d5
|
||||||
|
- smoother bar in admin task ui #4488ae2
|
||||||
|
- add noWrapper option #4f9b949
|
||||||
|
- add version metadata route #5393db3
|
||||||
|
- completed admin UI, with minor changes to backend #599da0e
|
||||||
|
- adjust gradient #5a1f841
|
||||||
|
- keep track of last connected #69e4c25
|
||||||
|
- added notification system w/ interwoven refactoring #6e6f09d
|
||||||
|
- content length header for chunk downloads #76bceb1
|
||||||
|
- add title to tab #7b0756c
|
||||||
|
- add button to open in admin panel #7b3b919
|
||||||
|
- client capability framework + peer API configuration #7d72a86
|
||||||
|
- customisable image carousel and new layout #937954f
|
||||||
|
- support more types #9b12d45
|
||||||
|
- generate a server certificate for mtls APIs #9c4b6f3
|
||||||
|
- new endpoints, ui and beginnings of main store page #9cbdcbc
|
||||||
|
- backend #a309651
|
||||||
|
- more subtle design improvements #a815542
|
||||||
|
- add aden's carousel pagination design #a86045c
|
||||||
|
- add header #a8a152e
|
||||||
|
- client side search #b50e27f
|
||||||
|
- new ws handler #bc0c47c
|
||||||
|
- user widget now redirects to actual page #bfafe02
|
||||||
|
- require lowercase usernames #d7160ab
|
||||||
|
- more ui improvements #e408ac5
|
||||||
|
- add modifying game descriptions #e505e58
|
||||||
|
- mobile nav #e5cf13f
|
||||||
|
- slightly improved game page #e796b46
|
||||||
|
- game carousel #ecc819e
|
||||||
|
- add enum dictionary type #f2e0182
|
||||||
|
- improved ux #f3ed0f6
|
||||||
|
- cleanup and raw accessors #f7d767d
|
||||||
|
- add support for overriding UMU id #fd4a7d1
|
||||||
|
- add .sh for linux #fe9373a
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
- quexeky <git@quexeky.dev>
|
||||||
|
- fixed manifest generation #03a37f7
|
||||||
|
- manual ci/cd #03b0b0c
|
||||||
|
- ability to fetch client certs for p2p #0a715fe
|
||||||
|
- disable tls in build #0f80fcd
|
||||||
|
- Updated README.md #17971e0
|
||||||
|
- Merge pull request #18 from Drop-OSS/develop
|
||||||
|
- initial work on metadata system #196f87c
|
||||||
|
- more ui #1bd19ad
|
||||||
|
- remove log statements #1d5e1bd
|
||||||
|
- small fixes & SSR disabled #1f575b2
|
||||||
|
- update information and setup guide #2236622
|
||||||
|
- metadata engine #22ac7f6
|
||||||
|
- Update CONTRIBUTING.md #2309407
|
||||||
|
- slight bug fixes and clean up #24a0d11
|
||||||
|
- almst complete admin ui and initial store designs #27070b6
|
||||||
|
- handshakes #2b4382d
|
||||||
|
- user mobile header #2e44ef3
|
||||||
|
- more consistent naming for globals #305de9f
|
||||||
|
- replaced markdown-it with micromark #31e8359
|
||||||
|
- fixes to store page for mobile clients #328b9ba
|
||||||
|
- game version re-ordering #329c74d
|
||||||
|
- verbose yarn install #36568c3
|
||||||
|
- patch for no version check in manifest generation #395219d
|
||||||
|
- migrate bcrypt to bcryptjs #3a51c9c
|
||||||
|
- added download chunk endpoint #3dd6062
|
||||||
|
- Update README.md #425934d
|
||||||
|
- build only ci #4273a20
|
||||||
|
- object storage + full permission system + testing #435551c
|
||||||
|
- rename admin socket session map #44c6028
|
||||||
|
- bump droplet and add vue carousel #46551f9
|
||||||
|
- version importing #46c8f0c
|
||||||
|
- back to yarn, with nuxt telemetry force disabled #46d35ad
|
||||||
|
- finished object endpoints #486bce8
|
||||||
|
- update dependencies and add note about optional dependencies #4fa771a
|
||||||
|
- use configuration from docs for ci/cd #52315d0
|
||||||
|
- slight fixes to register logic #583301f
|
||||||
|
- removed yarn.lock #584bcf1
|
||||||
|
- Version bump #5f29c28
|
||||||
|
- immutable application settings framework #5fe2036
|
||||||
|
- fixed docker daemon location #62a111b
|
||||||
|
- copy autodevops configuration #6328c24
|
||||||
|
- Delete .gitlab-ci.yml #69f341b
|
||||||
|
- admin ui shell #6b5e48d
|
||||||
|
- bump @drop/droplet version for windows developers #6ba5cdd
|
||||||
|
- Add LICENSE #6e2dc89
|
||||||
|
- custom dind #716eac7
|
||||||
|
- task API #718f5ba
|
||||||
|
- use gitlab ci variable declaration #7194d35
|
||||||
|
- move icons into dedicated folder #74fa671
|
||||||
|
- another stage of client authentication #7523e53
|
||||||
|
- refactoring #7869043
|
||||||
|
- moved windows logo into logos dir #789d3ba
|
||||||
|
- updated text colours across app #7a88f4c
|
||||||
|
- starting docs infra #7d2a1c6
|
||||||
|
- more cleaning #7e17626
|
||||||
|
- slight patch to rename query to be more consistent #7f4db0c
|
||||||
|
- move to raw docker #803752e
|
||||||
|
- server side and user client side completed for registration #848a611
|
||||||
|
- beginnings of download implementation #8674ac7
|
||||||
|
- more consistent naming for object handler #87230fb
|
||||||
|
- use autodevops build stage #886beb6
|
||||||
|
- Updated tailwind config #88c95d6
|
||||||
|
- change name of store file #8999303
|
||||||
|
- split prisma schemas #9011cf5
|
||||||
|
- client initiate #909432a
|
||||||
|
- more client routes to support Drop app update #91b7e10
|
||||||
|
- additional polish and QoL features #93bc143
|
||||||
|
- upload images to games #9b7ee4e
|
||||||
|
- migrate to pnpm due to ci/cd issues with yarn #9cb2d6d
|
||||||
|
- run yarn install in CI/CD non interactively #a208fbe
|
||||||
|
- completed game importing; partial work on version importing #a7c33e7
|
||||||
|
- remove canvas from dependencies #a8f58eb
|
||||||
|
- fix registry authentication #ad25d3e
|
||||||
|
- consolidate type utils #adb4b73
|
||||||
|
- Updated README.md #b0ef675
|
||||||
|
- add proper carousel to store page #b2ab827
|
||||||
|
- move to yarn v2 #b744671
|
||||||
|
- remove client API deadweight #b9ae26c
|
||||||
|
- add expires field #be6c30d
|
||||||
|
- ca groundwork #bfafd2a
|
||||||
|
- cleanup & polish #c355f6f
|
||||||
|
- remove bcrypt (debug) #c3914cc
|
||||||
|
- non rounded bottom #c4391d3
|
||||||
|
- failed gracefully on invalid chunk index #c4a3e4e
|
||||||
|
- update deploy template #c4a419f
|
||||||
|
- migrate to new droplet ca system #c4d8113
|
||||||
|
- docker based deployment #c5d00b4
|
||||||
|
- updated CONTRIBUTING.md #cd0d2bf
|
||||||
|
- update prisma version #ce0a9ab
|
||||||
|
- README update #ceacd84
|
||||||
|
- patch metadata handler #cf578bd
|
||||||
|
- Added SECURITY.md #d3d93b0
|
||||||
|
- finalised client APIs and authentication method #d4e2dc8
|
||||||
|
- Update README.md #db916bf
|
||||||
|
- object storage interface + utility functions #de388a9
|
||||||
|
- initial commit #e1a789f
|
||||||
|
- fixed task system #e1c1d7e
|
||||||
|
- Update file chunk.get.ts #e4339c3
|
||||||
|
- ui groundwork #e52f072
|
||||||
|
- Update changelog #eadcaa1
|
||||||
|
- check for no version in manifest generation #eb3f9f9
|
||||||
|
- break into single column store on lg devices #ecb381e
|
||||||
|
- better server side signin redirects #ef13b68
|
||||||
|
- patch signin #f3672f8
|
||||||
|
|
||||||
|
_changelog generated by_ [go-conventional-commits](https://github.com/joselitofilho/go-conventional-commits)
|
||||||
|
|
||||||
|
## Release 0.2.0-beta
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- fix recursive dirs util #02d6346
|
||||||
|
- Fix username length requirement #0a5a649
|
||||||
|
- remove dynamic imports #0f10626
|
||||||
|
- fix for missing developers or publishers #25fc957
|
||||||
|
- split prisma schemas #2859005
|
||||||
|
- results are returned alphabetically #33d3770
|
||||||
|
- update prisma schemas #36776cc
|
||||||
|
- removed global flag #43e32b4
|
||||||
|
- properly disconnect websockets from task handler #5358f1f
|
||||||
|
- follow best practices #54c5d55
|
||||||
|
- future lenience #5c78b20
|
||||||
|
- fix width of token breaking things #61d88c3
|
||||||
|
- fixed websocket authentication #62ea9a1
|
||||||
|
- fix delta manifest generation #6df560c
|
||||||
|
- admin invitation w/ system user #8463e35
|
||||||
|
- properly import icons #8945196
|
||||||
|
- prisma create footprint #952ece8
|
||||||
|
- game panel now always shows 3 lines exactly #9c2249e
|
||||||
|
- remove unnecessary import #a361c38
|
||||||
|
- fix disconnect code #a8f2106
|
||||||
|
- fix types #b511b40
|
||||||
|
- add drop-base as git submodule #b75ebd1
|
||||||
|
- Update README.md with discord link #c6bb21d
|
||||||
|
- fix expires requirement in the admin endpoint #c7b675f
|
||||||
|
- fix always being created as admin #c7eb11a
|
||||||
|
- moved icons and created PlatformClient so we can use the enum on the frontend #cada630
|
||||||
|
- recurse submodules #db103de
|
||||||
|
- fix FATAL: "root"... message #dbb315a
|
||||||
|
- only show versions that are directories #ef8f3ae
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- update prisma & delete games #089c3e0
|
||||||
|
- manual handshake #12e3125
|
||||||
|
- fetch game endpoint #1f4d075
|
||||||
|
- under the hood organisation and consolidation #26a31f6
|
||||||
|
- 'no images' slide on image carousel #28baabc
|
||||||
|
- improve feedback when metadata fails #2c19e13
|
||||||
|
- introduction of 'system user' #2c21a23
|
||||||
|
- change name, description and icon #2cfe75a
|
||||||
|
- 'manual' metadata provider #2f52a16
|
||||||
|
- add disabled state #38fc6b8
|
||||||
|
- overhauled version importing #39d7ce7
|
||||||
|
- automatically create library folder if it doesn't exist #39fe9d5
|
||||||
|
- smoother bar in admin task ui #4488ae2
|
||||||
|
- add noWrapper option #4f9b949
|
||||||
|
- add version metadata route #5393db3
|
||||||
|
- completed admin UI, with minor changes to backend #599da0e
|
||||||
|
- adjust gradient #5a1f841
|
||||||
|
- keep track of last connected #69e4c25
|
||||||
|
- added notification system w/ interwoven refactoring #6e6f09d
|
||||||
|
- content length header for chunk downloads #76bceb1
|
||||||
|
- add title to tab #7b0756c
|
||||||
|
- add button to open in admin panel #7b3b919
|
||||||
|
- client capability framework + peer API configuration #7d72a86
|
||||||
|
- customisable image carousel and new layout #937954f
|
||||||
|
- support more types #9b12d45
|
||||||
|
- generate a server certificate for mtls APIs #9c4b6f3
|
||||||
|
- new endpoints, ui and beginnings of main store page #9cbdcbc
|
||||||
|
- backend #a309651
|
||||||
|
- more subtle design improvements #a815542
|
||||||
|
- add aden's carousel pagination design #a86045c
|
||||||
|
- add header #a8a152e
|
||||||
|
- client side search #b50e27f
|
||||||
|
- new ws handler #bc0c47c
|
||||||
|
- user widget now redirects to actual page #bfafe02
|
||||||
|
- require lowercase usernames #d7160ab
|
||||||
|
- more ui improvements #e408ac5
|
||||||
|
- add modifying game descriptions #e505e58
|
||||||
|
- mobile nav #e5cf13f
|
||||||
|
- slightly improved game page #e796b46
|
||||||
|
- game carousel #ecc819e
|
||||||
|
- add enum dictionary type #f2e0182
|
||||||
|
- improved ux #f3ed0f6
|
||||||
|
- cleanup and raw accessors #f7d767d
|
||||||
|
- add support for overriding UMU id #fd4a7d1
|
||||||
|
- add .sh for linux #fe9373a
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
- quexeky <git@quexeky.dev>
|
||||||
|
- fixed manifest generation #03a37f7
|
||||||
|
- manual ci/cd #03b0b0c
|
||||||
|
- ability to fetch client certs for p2p #0a715fe
|
||||||
|
- disable tls in build #0f80fcd
|
||||||
|
- Updated README.md #17971e0
|
||||||
|
- Merge pull request #18 from Drop-OSS/develop
|
||||||
|
- initial work on metadata system #196f87c
|
||||||
|
- more ui #1bd19ad
|
||||||
|
- remove log statements #1d5e1bd
|
||||||
|
- small fixes & SSR disabled #1f575b2
|
||||||
|
- update information and setup guide #2236622
|
||||||
|
- metadata engine #22ac7f6
|
||||||
|
- Update CONTRIBUTING.md #2309407
|
||||||
|
- slight bug fixes and clean up #24a0d11
|
||||||
|
- almst complete admin ui and initial store designs #27070b6
|
||||||
|
- handshakes #2b4382d
|
||||||
|
- user mobile header #2e44ef3
|
||||||
|
- more consistent naming for globals #305de9f
|
||||||
|
- replaced markdown-it with micromark #31e8359
|
||||||
|
- fixes to store page for mobile clients #328b9ba
|
||||||
|
- game version re-ordering #329c74d
|
||||||
|
- verbose yarn install #36568c3
|
||||||
|
- patch for no version check in manifest generation #395219d
|
||||||
|
- migrate bcrypt to bcryptjs #3a51c9c
|
||||||
|
- added download chunk endpoint #3dd6062
|
||||||
|
- Update README.md #425934d
|
||||||
|
- build only ci #4273a20
|
||||||
|
- object storage + full permission system + testing #435551c
|
||||||
|
- rename admin socket session map #44c6028
|
||||||
|
- bump droplet and add vue carousel #46551f9
|
||||||
|
- version importing #46c8f0c
|
||||||
|
- back to yarn, with nuxt telemetry force disabled #46d35ad
|
||||||
|
- finished object endpoints #486bce8
|
||||||
|
- update dependencies and add note about optional dependencies #4fa771a
|
||||||
|
- use configuration from docs for ci/cd #52315d0
|
||||||
|
- slight fixes to register logic #583301f
|
||||||
|
- removed yarn.lock #584bcf1
|
||||||
|
- Version bump #5f29c28
|
||||||
|
- immutable application settings framework #5fe2036
|
||||||
|
- fixed docker daemon location #62a111b
|
||||||
|
- copy autodevops configuration #6328c24
|
||||||
|
- Delete .gitlab-ci.yml #69f341b
|
||||||
|
- admin ui shell #6b5e48d
|
||||||
|
- bump @drop/droplet version for windows developers #6ba5cdd
|
||||||
|
- Add LICENSE #6e2dc89
|
||||||
|
- custom dind #716eac7
|
||||||
|
- task API #718f5ba
|
||||||
|
- use gitlab ci variable declaration #7194d35
|
||||||
|
- move icons into dedicated folder #74fa671
|
||||||
|
- another stage of client authentication #7523e53
|
||||||
|
- refactoring #7869043
|
||||||
|
- moved windows logo into logos dir #789d3ba
|
||||||
|
- updated text colours across app #7a88f4c
|
||||||
|
- starting docs infra #7d2a1c6
|
||||||
|
- more cleaning #7e17626
|
||||||
|
- slight patch to rename query to be more consistent #7f4db0c
|
||||||
|
- move to raw docker #803752e
|
||||||
|
- server side and user client side completed for registration #848a611
|
||||||
|
- beginnings of download implementation #8674ac7
|
||||||
|
- more consistent naming for object handler #87230fb
|
||||||
|
- use autodevops build stage #886beb6
|
||||||
|
- Updated tailwind config #88c95d6
|
||||||
|
- change name of store file #8999303
|
||||||
|
- split prisma schemas #9011cf5
|
||||||
|
- client initiate #909432a
|
||||||
|
- more client routes to support Drop app update #91b7e10
|
||||||
|
- additional polish and QoL features #93bc143
|
||||||
|
- upload images to games #9b7ee4e
|
||||||
|
- migrate to pnpm due to ci/cd issues with yarn #9cb2d6d
|
||||||
|
- run yarn install in CI/CD non interactively #a208fbe
|
||||||
|
- completed game importing; partial work on version importing #a7c33e7
|
||||||
|
- remove canvas from dependencies #a8f58eb
|
||||||
|
- fix registry authentication #ad25d3e
|
||||||
|
- consolidate type utils #adb4b73
|
||||||
|
- Updated README.md #b0ef675
|
||||||
|
- add proper carousel to store page #b2ab827
|
||||||
|
- move to yarn v2 #b744671
|
||||||
|
- remove client API deadweight #b9ae26c
|
||||||
|
- add expires field #be6c30d
|
||||||
|
- ca groundwork #bfafd2a
|
||||||
|
- cleanup & polish #c355f6f
|
||||||
|
- remove bcrypt (debug) #c3914cc
|
||||||
|
- non rounded bottom #c4391d3
|
||||||
|
- failed gracefully on invalid chunk index #c4a3e4e
|
||||||
|
- update deploy template #c4a419f
|
||||||
|
- migrate to new droplet ca system #c4d8113
|
||||||
|
- docker based deployment #c5d00b4
|
||||||
|
- updated CONTRIBUTING.md #cd0d2bf
|
||||||
|
- update prisma version #ce0a9ab
|
||||||
|
- README update #ceacd84
|
||||||
|
- patch metadata handler #cf578bd
|
||||||
|
- Added SECURITY.md #d3d93b0
|
||||||
|
- finalised client APIs and authentication method #d4e2dc8
|
||||||
|
- Update README.md #db916bf
|
||||||
|
- object storage interface + utility functions #de388a9
|
||||||
|
- initial commit #e1a789f
|
||||||
|
- fixed task system #e1c1d7e
|
||||||
|
- Update file chunk.get.ts #e4339c3
|
||||||
|
- ui groundwork #e52f072
|
||||||
|
- Update changelog #eadcaa1
|
||||||
|
- check for no version in manifest generation #eb3f9f9
|
||||||
|
- break into single column store on lg devices #ecb381e
|
||||||
|
- better server side signin redirects #ef13b68
|
||||||
|
- patch signin #f3672f8
|
||||||
|
|
||||||
|
_changelog generated by_ [go-conventional-commits](https://github.com/joselitofilho/go-conventional-commits)
|
||||||
|
|
||||||
|
## Release 0.1.0-beta
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- remove dynamic imports #0f10626
|
||||||
|
- fix for missing developers or publishers #25fc957
|
||||||
|
- split prisma schemas #2859005
|
||||||
|
- results are returned alphabetically #33d3770
|
||||||
|
- properly disconnect websockets from task handler #5358f1f
|
||||||
|
- follow best practices #54c5d55
|
||||||
|
- future lenience #5c78b20
|
||||||
|
- fixed websocket authentication #62ea9a1
|
||||||
|
- fix delta manifest generation #6df560c
|
||||||
|
- admin invitation w/ system user #8463e35
|
||||||
|
- properly import icons #8945196
|
||||||
|
- prisma create footprint #952ece8
|
||||||
|
- game panel now always shows 3 lines exactly #9c2249e
|
||||||
|
- remove unnecessary import #a361c38
|
||||||
|
- fix types #b511b40
|
||||||
|
- fix expires requirement in the admin endpoint #c7b675f
|
||||||
|
- moved icons and created PlatformClient so we can use the enum on the frontend #cada630
|
||||||
|
- only show versions that are directories #ef8f3ae
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- update prisma & delete games #089c3e0
|
||||||
|
- fetch game endpoint #1f4d075
|
||||||
|
- under the hood organisation and consolidation #26a31f6
|
||||||
|
- introduction of 'system user' #2c21a23
|
||||||
|
- automatically create library folder if it doesn't exist #39fe9d5
|
||||||
|
- smoother bar in admin task ui #4488ae2
|
||||||
|
- add version metadata route #5393db3
|
||||||
|
- completed admin UI, with minor changes to backend #599da0e
|
||||||
|
- keep track of last connected #69e4c25
|
||||||
|
- added notification system w/ interwoven refactoring #6e6f09d
|
||||||
|
- content length header for chunk downloads #76bceb1
|
||||||
|
- add title to tab #7b0756c
|
||||||
|
- add button to open in admin panel #7b3b919
|
||||||
|
- client capability framework + peer API configuration #7d72a86
|
||||||
|
- generate a server certificate for mtls APIs #9c4b6f3
|
||||||
|
- new endpoints, ui and beginnings of main store page #9cbdcbc
|
||||||
|
- more subtle design improvements #a815542
|
||||||
|
- add header #a8a152e
|
||||||
|
- client side search #b50e27f
|
||||||
|
- new ws handler #bc0c47c
|
||||||
|
- user widget now redirects to actual page #bfafe02
|
||||||
|
- require lowercase usernames #d7160ab
|
||||||
|
- more ui improvements #e408ac5
|
||||||
|
- slightly improved game page #e796b46
|
||||||
|
- game carousel #ecc819e
|
||||||
|
- add enum dictionary type #f2e0182
|
||||||
|
- cleanup and raw accessors #f7d767d
|
||||||
|
- add support for overriding UMU id #fd4a7d1
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
- quexeky <git@quexeky.dev>
|
||||||
|
- fixed manifest generation #03a37f7
|
||||||
|
- manual ci/cd #03b0b0c
|
||||||
|
- ability to fetch client certs for p2p #0a715fe
|
||||||
|
- disable tls in build #0f80fcd
|
||||||
|
- Updated README.md #17971e0
|
||||||
|
- initial work on metadata system #196f87c
|
||||||
|
- more ui #1bd19ad
|
||||||
|
- remove log statements #1d5e1bd
|
||||||
|
- small fixes & SSR disabled #1f575b2
|
||||||
|
- update information and setup guide #2236622
|
||||||
|
- metadata engine #22ac7f6
|
||||||
|
- Update CONTRIBUTING.md #2309407
|
||||||
|
- slight bug fixes and clean up #24a0d11
|
||||||
|
- almst complete admin ui and initial store designs #27070b6
|
||||||
|
- handshakes #2b4382d
|
||||||
|
- user mobile header #2e44ef3
|
||||||
|
- more consistent naming for globals #305de9f
|
||||||
|
- replaced markdown-it with micromark #31e8359
|
||||||
|
- fixes to store page for mobile clients #328b9ba
|
||||||
|
- game version re-ordering #329c74d
|
||||||
|
- verbose yarn install #36568c3
|
||||||
|
- patch for no version check in manifest generation #395219d
|
||||||
|
- migrate bcrypt to bcryptjs #3a51c9c
|
||||||
|
- added download chunk endpoint #3dd6062
|
||||||
|
- Update README.md #425934d
|
||||||
|
- build only ci #4273a20
|
||||||
|
- object storage + full permission system + testing #435551c
|
||||||
|
- rename admin socket session map #44c6028
|
||||||
|
- bump droplet and add vue carousel #46551f9
|
||||||
|
- version importing #46c8f0c
|
||||||
|
- back to yarn, with nuxt telemetry force disabled #46d35ad
|
||||||
|
- finished object endpoints #486bce8
|
||||||
|
- update dependencies and add note about optional dependencies #4fa771a
|
||||||
|
- use configuration from docs for ci/cd #52315d0
|
||||||
|
- slight fixes to register logic #583301f
|
||||||
|
- removed yarn.lock #584bcf1
|
||||||
|
- Version bump #5f29c28
|
||||||
|
- immutable application settings framework #5fe2036
|
||||||
|
- fixed docker daemon location #62a111b
|
||||||
|
- copy autodevops configuration #6328c24
|
||||||
|
- Delete .gitlab-ci.yml #69f341b
|
||||||
|
- admin ui shell #6b5e48d
|
||||||
|
- bump @drop/droplet version for windows developers #6ba5cdd
|
||||||
|
- Add LICENSE #6e2dc89
|
||||||
|
- task API #718f5ba
|
||||||
|
- use gitlab ci variable declaration #7194d35
|
||||||
|
- move icons into dedicated folder #74fa671
|
||||||
|
- another stage of client authentication #7523e53
|
||||||
|
- refactoring #7869043
|
||||||
|
- moved windows logo into logos dir #789d3ba
|
||||||
|
- updated text colours across app #7a88f4c
|
||||||
|
- starting docs infra #7d2a1c6
|
||||||
|
- more cleaning #7e17626
|
||||||
|
- slight patch to rename query to be more consistent #7f4db0c
|
||||||
|
- move to raw docker #803752e
|
||||||
|
- server side and user client side completed for registration #848a611
|
||||||
|
- beginnings of download implementation #8674ac7
|
||||||
|
- more consistent naming for object handler #87230fb
|
||||||
|
- use autodevops build stage #886beb6
|
||||||
|
- Updated tailwind config #88c95d6
|
||||||
|
- change name of store file #8999303
|
||||||
|
- split prisma schemas #9011cf5
|
||||||
|
- client initiate #909432a
|
||||||
|
- more client routes to support Drop app update #91b7e10
|
||||||
|
- additional polish and QoL features #93bc143
|
||||||
|
- upload images to games #9b7ee4e
|
||||||
|
- migrate to pnpm due to ci/cd issues with yarn #9cb2d6d
|
||||||
|
- run yarn install in CI/CD non interactively #a208fbe
|
||||||
|
- completed game importing; partial work on version importing #a7c33e7
|
||||||
|
- remove canvas from dependencies #a8f58eb
|
||||||
|
- fix registry authentication #ad25d3e
|
||||||
|
- consolidate type utils #adb4b73
|
||||||
|
- Updated README.md #b0ef675
|
||||||
|
- add proper carousel to store page #b2ab827
|
||||||
|
- move to yarn v2 #b744671
|
||||||
|
- remove client API deadweight #b9ae26c
|
||||||
|
- add expires field #be6c30d
|
||||||
|
- ca groundwork #bfafd2a
|
||||||
|
- cleanup & polish #c355f6f
|
||||||
|
- remove bcrypt (debug) #c3914cc
|
||||||
|
- non rounded bottom #c4391d3
|
||||||
|
- failed gracefully on invalid chunk index #c4a3e4e
|
||||||
|
- update deploy template #c4a419f
|
||||||
|
- migrate to new droplet ca system #c4d8113
|
||||||
|
- docker based deployment #c5d00b4
|
||||||
|
- updated CONTRIBUTING.md #cd0d2bf
|
||||||
|
- update prisma version #ce0a9ab
|
||||||
|
- README update #ceacd84
|
||||||
|
- patch metadata handler #cf578bd
|
||||||
|
- Added SECURITY.md #d3d93b0
|
||||||
|
- finalised client APIs and authentication method #d4e2dc8
|
||||||
|
- Update README.md #db916bf
|
||||||
|
- object storage interface + utility functions #de388a9
|
||||||
|
- initial commit #e1a789f
|
||||||
|
- fixed task system #e1c1d7e
|
||||||
|
- Update file chunk.get.ts #e4339c3
|
||||||
|
- ui groundwork #e52f072
|
||||||
|
- check for no version in manifest generation #eb3f9f9
|
||||||
|
- break into single column store on lg devices #ecb381e
|
||||||
|
- better server side signin redirects #ef13b68
|
||||||
|
- patch signin #f3672f8
|
||||||
|
|
||||||
|
_changelog generated by_ [go-conventional-commits](https://github.com/joselitofilho/go-conventional-commits)
|
||||||
@@ -1 +0,0 @@
|
|||||||
use flake
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/target
|
|
||||||
logs/
|
|
||||||
.vscode
|
|
||||||
.direnv
|
|
||||||
Generated
-3396
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "downpour"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0.100"
|
|
||||||
async-trait = "0.1.89"
|
|
||||||
chrono = "0.4.43"
|
|
||||||
clap = { version = "4.5.54", features = ["derive"] }
|
|
||||||
console = "0.16.2"
|
|
||||||
dialoguer = "0.12.0"
|
|
||||||
dirs = "6.0.0"
|
|
||||||
droplet-rs = { path = "../libraries/droplet" }
|
|
||||||
fern = { version = "0.7.1", features = ["colored"] }
|
|
||||||
futures = "0.3.31"
|
|
||||||
indicatif = "0.18.3"
|
|
||||||
log = "0.4.29"
|
|
||||||
opendal = { version = "0.55.0", features = ["services-s3"] }
|
|
||||||
rand = "0.9.2"
|
|
||||||
reqwest = { version = "0.13.1", features = ["json"] }
|
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
|
||||||
serde_json = "1.0.148"
|
|
||||||
tokio = { version = "1.48.0", features = ["fs", "macros"] }
|
|
||||||
tokio-util = { version = "0.7.18", features = ["compat"] }
|
|
||||||
url = "2.5.8"
|
|
||||||
webbrowser = "1.0.6"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# CLI (`downpour`)
|
|
||||||
|
|
||||||
The cli way to access Drop. Used for admin tasks that require local access, like uploading game content.
|
|
||||||
Generated
-96
@@ -1,96 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731533236,
|
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1768564909,
|
|
||||||
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1744536153,
|
|
||||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"rust-overlay": "rust-overlay"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rust-overlay": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1768704795,
|
|
||||||
"narHash": "sha256-Y33TAp2BHEcuspYvcmBXXD0qdvjftv73PwyKTDOjoSY=",
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"rev": "4b7472a78857ac789fb26616040f55cfcbd36c6e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
description = "Drop-OSS app development environment";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs =
|
|
||||||
{
|
|
||||||
self,
|
|
||||||
nixpkgs,
|
|
||||||
flake-utils,
|
|
||||||
rust-overlay,
|
|
||||||
}:
|
|
||||||
flake-utils.lib.eachDefaultSystem (
|
|
||||||
system:
|
|
||||||
let
|
|
||||||
overlays = [ (import rust-overlay) ];
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system overlays;
|
|
||||||
};
|
|
||||||
libraries = with pkgs; [
|
|
||||||
glib
|
|
||||||
glibc
|
|
||||||
openssl
|
|
||||||
];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
nativeBuildInputs = with pkgs; [
|
|
||||||
pkg-config
|
|
||||||
git
|
|
||||||
rust-bin.nightly.latest.default
|
|
||||||
rust-analyzer
|
|
||||||
cargo-expand
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
buildInputs = libraries;
|
|
||||||
|
|
||||||
shellHook = ''
|
|
||||||
export LD_LIBRARY_PATH="${
|
|
||||||
pkgs.lib.makeLibraryPath libraries
|
|
||||||
}:$LD_LIBRARY_PATH"
|
|
||||||
echo "Downpour development environment loaded"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
# Downpour CLI spec
|
|
||||||
`downpour [command] --opts`
|
|
||||||
## Commands:
|
|
||||||
- new <path/s3 name> <public endpoint> - creates/initalizes a depot at the endpoint. Creates manifest.json and speedtest
|
|
||||||
- connect <s3 endpoint> <key> <secret> [name] - connects to an s3 endpoint and saves the endpoint to some sort of credentials file. Name is either as provided or the hostname of the endpoint
|
|
||||||
- upload <game id> <localpath> <path/s3 name> - uploads game as described before. Should fail if depot isn't initialized with new from above
|
|
||||||
- copy <game id> <version id> <src path/s3 name> <dest path/s3 name> - copies between two depots
|
|
||||||
- mark [exists/absent] <game id> <version id> <path/s3 name> - modifies depot's manifest.json to show content exists or is absent without copying (for third party copies)
|
|
||||||
- rename <public endpoint> <new public endpoint> - renames an endpoint [NEEDS API ROUTES - can't do yet]
|
|
||||||
- delete <public endpoint> - delete an endpoint [NEEDS API ROUTES - can't do yet]
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
|
||||||
|
|
||||||
use crate::{commands::connect::config_option::ConfigOptionCli, interactive_variable};
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(version, about, long_about = None)]
|
|
||||||
pub struct Cli {
|
|
||||||
#[command(subcommand)]
|
|
||||||
pub command: Commands,
|
|
||||||
|
|
||||||
/// Specify data file path
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub data: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum Commands {
|
|
||||||
/// Configures downpour endpoints
|
|
||||||
Connect {
|
|
||||||
#[arg(short, long)]
|
|
||||||
name: Option<String>,
|
|
||||||
#[command(subcommand)]
|
|
||||||
option: ConfigOptionCli,
|
|
||||||
},
|
|
||||||
/// Uploads new game version to depot
|
|
||||||
Upload {
|
|
||||||
#[clap(flatten)]
|
|
||||||
info: UploadInfoCli,
|
|
||||||
#[arg(short, long)]
|
|
||||||
/// Alias of a given connection
|
|
||||||
name: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args)]
|
|
||||||
pub struct UploadInfo {
|
|
||||||
pub path: String,
|
|
||||||
pub game_id: String,
|
|
||||||
pub version_id: String,
|
|
||||||
}
|
|
||||||
#[derive(Args)]
|
|
||||||
pub struct UploadInfoCli {
|
|
||||||
/// Relative path to new version files
|
|
||||||
#[arg(short, long, default_value_t = String::from("."))]
|
|
||||||
pub path: String,
|
|
||||||
/// ID of game to attach to
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub game_id: Option<String>,
|
|
||||||
/// Version ID to attach to
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub version_id: Option<String>,
|
|
||||||
}
|
|
||||||
impl UploadInfoCli {
|
|
||||||
pub fn interactive_configure(self) -> UploadInfo {
|
|
||||||
let path = self.path;
|
|
||||||
interactive_variable!(self, game_id, "Game ID");
|
|
||||||
interactive_variable!(self, version_id, "Version ID");
|
|
||||||
UploadInfo {
|
|
||||||
path,
|
|
||||||
game_id,
|
|
||||||
version_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum UploadStyle {
|
|
||||||
S3,
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
commands::connect::{
|
|
||||||
config_option::{ConfigOption, ConfigOptionCli},
|
|
||||||
configurable::Configure,
|
|
||||||
speedtest::{SPEEDTEST_PATH, Speedtest},
|
|
||||||
},
|
|
||||||
manifest::DepotManifest,
|
|
||||||
};
|
|
||||||
use dialoguer::{Confirm, theme::ColorfulTheme};
|
|
||||||
use futures::AsyncWriteExt;
|
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
use log::{debug, info};
|
|
||||||
use opendal::Operator;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::{collections::HashMap, fs, ops::Not};
|
|
||||||
use tokio_util::compat::FuturesAsyncWriteCompatExt;
|
|
||||||
|
|
||||||
const CONFIG_DIR: &str = "downpour/config.json";
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Config {
|
|
||||||
configurations: HashMap<String, ConfigOption>,
|
|
||||||
active: Option<String>,
|
|
||||||
}
|
|
||||||
impl Config {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
configurations: HashMap::new(),
|
|
||||||
active: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn exists(&self, name: &String) -> bool {
|
|
||||||
self.configurations.contains_key(name)
|
|
||||||
}
|
|
||||||
pub fn save(&self) -> anyhow::Result<()> {
|
|
||||||
let json = serde_json::to_string(self)?;
|
|
||||||
let save_path = dirs::config_dir()
|
|
||||||
.expect("Apparently your home directory doesn't exist") // Should probably formalise that error
|
|
||||||
.join(CONFIG_DIR);
|
|
||||||
fs::create_dir_all(save_path.parent().unwrap())?;
|
|
||||||
fs::write(save_path, json)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn read() -> Self {
|
|
||||||
let save_path = dirs::config_dir()
|
|
||||||
.expect("Apparently your home directory doesn't exist") // Should probably formalise that error
|
|
||||||
.join(CONFIG_DIR);
|
|
||||||
if fs::exists(&save_path)
|
|
||||||
.unwrap_or_else(|_| panic!("Could not read save path {:#?}", &save_path))
|
|
||||||
{
|
|
||||||
serde_json::from_str(&fs::read_to_string(save_path).unwrap()).unwrap()
|
|
||||||
} else {
|
|
||||||
Config::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn add_item(&mut self, name: String, object: ConfigOption) {
|
|
||||||
if matches!(object, ConfigOption::S3(..)) {
|
|
||||||
self.active = Some(name.clone())
|
|
||||||
}
|
|
||||||
self.configurations.insert(name, object);
|
|
||||||
self.save().expect("Failed to save config");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_active(&self) -> Option<&ConfigOption> {
|
|
||||||
if let Some(active) = &self.active {
|
|
||||||
self.configurations.get(active)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get<T: AsRef<str>>(&self, name: T) -> Option<&ConfigOption> {
|
|
||||||
self.configurations.get(name.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn manage_configuration(
|
|
||||||
config: &mut Config,
|
|
||||||
name: Option<String>,
|
|
||||||
option: ConfigOptionCli,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let mut name = name;
|
|
||||||
if let Some(name) = &name
|
|
||||||
&& config.exists(name)
|
|
||||||
{
|
|
||||||
let confirm = Confirm::with_theme(&ColorfulTheme::default())
|
|
||||||
.with_prompt(format!(
|
|
||||||
"An entry already exists with the name \"{}\". Would you like to overwrite it?",
|
|
||||||
name
|
|
||||||
))
|
|
||||||
.interact()?;
|
|
||||||
if !confirm {
|
|
||||||
return Err(anyhow::anyhow!("User cancelled action"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let config_option = match option {
|
|
||||||
ConfigOptionCli::S3(s3_config_cli) => s3_config_cli.clone().configure(&mut name).await?,
|
|
||||||
};
|
|
||||||
let name = name.expect("Default name was not provided by ConfigOption. This is a bug");
|
|
||||||
config.add_item(name, config_option.clone());
|
|
||||||
let operator = config_option.build()?;
|
|
||||||
|
|
||||||
generate_manifest(&operator).await?;
|
|
||||||
info!("Finished uploading manifest");
|
|
||||||
generate_speedtest(&operator).await?;
|
|
||||||
info!("Finished uploading speedtest");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn generate_speedtest(operator: &Operator) -> anyhow::Result<()> {
|
|
||||||
// Workaround to operator.exists("...") also logging a 404 warning
|
|
||||||
let lister = operator.list_with(SPEEDTEST_PATH).limit(1).await?;
|
|
||||||
if lister.is_empty().not() {
|
|
||||||
info!("Speedtest already exists on Depot. Skipping speedtest upload...");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let mut writer = operator
|
|
||||||
.writer(SPEEDTEST_PATH)
|
|
||||||
.await?
|
|
||||||
.into_futures_async_write()
|
|
||||||
.compat_write();
|
|
||||||
|
|
||||||
let progress_bar = ProgressBar::new(10_000).with_style(
|
|
||||||
ProgressStyle::default_bar()
|
|
||||||
.template("[{elapsed_precise}] [ETA {eta}] {bar} {percent_precise}%")
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut reader = Speedtest::new(|progress| {
|
|
||||||
let progress_int = (progress * 100f32).round() as u64;
|
|
||||||
progress_bar.set_position(progress_int);
|
|
||||||
});
|
|
||||||
let written = tokio::io::copy(&mut reader, &mut writer).await?;
|
|
||||||
progress_bar.finish();
|
|
||||||
debug!("Wrote {} bytes to {:?}", written, operator.info());
|
|
||||||
writer.into_inner().close().await?;
|
|
||||||
debug!("Closed writer");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn generate_manifest(operator: &Operator) -> anyhow::Result<()> {
|
|
||||||
let lister = operator.list_with("manifest.json").limit(1).await?;
|
|
||||||
if lister.is_empty().not() {
|
|
||||||
info!("Manifest already exists on Depot. Skipping manifest upload...");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let data = DepotManifest::new();
|
|
||||||
operator
|
|
||||||
.write("manifest.json", serde_json::to_string(&data)?)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
use clap::Subcommand;
|
|
||||||
use opendal::{Operator, layers::LoggingLayer};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
commands::connect::s3::{S3Config, S3ConfigCli},
|
|
||||||
operator_builder::OperatorBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Subcommand, Clone)]
|
|
||||||
pub enum ConfigOptionCli {
|
|
||||||
// Connect to any S3-compatible endpoint
|
|
||||||
S3(S3ConfigCli),
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
pub enum ConfigOption {
|
|
||||||
S3(S3Config),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigOption {
|
|
||||||
pub fn build(&self) -> anyhow::Result<Operator> {
|
|
||||||
Ok(match self {
|
|
||||||
ConfigOption::S3(s3_config) => s3_config.build()?,
|
|
||||||
}
|
|
||||||
.layer(LoggingLayer::default()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
use crate::commands::connect::config_option::ConfigOption;
|
|
||||||
|
|
||||||
pub trait Configure {
|
|
||||||
async fn configure(self, name: &mut Option<String>) -> anyhow::Result<ConfigOption>;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use dialoguer::{Input, theme::ColorfulTheme};
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! interactive_variable {
|
|
||||||
($value:ident, $var:ident, $prompt:expr) => {
|
|
||||||
let $var = if let Some($var) = $value.$var {
|
|
||||||
$var
|
|
||||||
} else {
|
|
||||||
$crate::commands::connect::interactive::query_variable($prompt).unwrap()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! interactive_optional_variable {
|
|
||||||
($value:ident, $var:ident, $prompt:expr) => {
|
|
||||||
let $var = if let Some($var) = $value.$var {
|
|
||||||
Some($var)
|
|
||||||
} else {
|
|
||||||
$crate::commands::connect::interactive::query_optional_variable($prompt).unwrap()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub fn query_variable<T: Clone + FromStr + ToString>(prompt: impl ToString) -> dialoguer::Result<T>
|
|
||||||
where
|
|
||||||
<T as FromStr>::Err: ToString,
|
|
||||||
{
|
|
||||||
Input::with_theme(&ColorfulTheme::default())
|
|
||||||
.with_prompt(prompt.to_string())
|
|
||||||
.interact_text()
|
|
||||||
}
|
|
||||||
pub fn query_optional_variable<T: Clone + FromStr + ToString>(
|
|
||||||
prompt: impl ToString,
|
|
||||||
) -> dialoguer::Result<Option<T>>
|
|
||||||
where
|
|
||||||
<T as FromStr>::Err: ToString,
|
|
||||||
{
|
|
||||||
let input: T = Input::with_theme(&ColorfulTheme::default())
|
|
||||||
.with_prompt(prompt.to_string())
|
|
||||||
.allow_empty(true)
|
|
||||||
.interact_text()?;
|
|
||||||
if input.to_string().is_empty() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
Ok(Some(input))
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
pub mod config;
|
|
||||||
pub mod configurable;
|
|
||||||
pub mod s3;
|
|
||||||
#[macro_use]
|
|
||||||
pub mod interactive;
|
|
||||||
pub mod config_option;
|
|
||||||
pub mod speedtest;
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
use clap::Args;
|
|
||||||
use opendal::Operator;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
commands::connect::{config_option::ConfigOption, configurable::Configure},
|
|
||||||
interactive_variable,
|
|
||||||
operator_builder::OperatorBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
|
||||||
pub struct S3ConfigCli {
|
|
||||||
key_id: Option<String>,
|
|
||||||
secret_key: Option<String>,
|
|
||||||
endpoint: Option<String>,
|
|
||||||
region: Option<String>,
|
|
||||||
bucket_name: Option<String>,
|
|
||||||
root: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
pub struct S3Config {
|
|
||||||
key_id: String,
|
|
||||||
secret_key: String,
|
|
||||||
endpoint: String,
|
|
||||||
region: String,
|
|
||||||
bucket_name: String,
|
|
||||||
root: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Configure for S3ConfigCli {
|
|
||||||
async fn configure(self, name: &mut Option<String>) -> anyhow::Result<ConfigOption> {
|
|
||||||
interactive_variable!(self, key_id, "S3 Key ID");
|
|
||||||
interactive_variable!(self, secret_key, "S3 Secret Key");
|
|
||||||
interactive_variable!(self, region, "S3 Region");
|
|
||||||
interactive_variable!(self, bucket_name, "S3 Bucket Name");
|
|
||||||
interactive_variable!(self, endpoint, "S3 Endpoint");
|
|
||||||
if let None = name {
|
|
||||||
*name = Some(endpoint.clone());
|
|
||||||
}
|
|
||||||
Ok(ConfigOption::S3(S3Config {
|
|
||||||
secret_key,
|
|
||||||
key_id,
|
|
||||||
region,
|
|
||||||
bucket_name,
|
|
||||||
endpoint,
|
|
||||||
root: self.root,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OperatorBuilder for S3Config {
|
|
||||||
fn build(&self) -> anyhow::Result<Operator> {
|
|
||||||
let builder = opendal::services::S3::default()
|
|
||||||
.access_key_id(&self.key_id)
|
|
||||||
.secret_access_key(&self.secret_key)
|
|
||||||
.region(&self.region)
|
|
||||||
.endpoint(&self.endpoint)
|
|
||||||
.root(self.root.as_deref().unwrap_or("/"))
|
|
||||||
.bucket(&self.bucket_name)
|
|
||||||
.disable_config_load();
|
|
||||||
|
|
||||||
let op: Operator = Operator::new(builder)?.finish();
|
|
||||||
|
|
||||||
Ok(op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
use rand::{RngCore, SeedableRng, rng, rngs::StdRng};
|
|
||||||
use tokio::io::AsyncRead;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Speedtest<F: Fn(f32)> {
|
|
||||||
core: rand::rngs::StdRng,
|
|
||||||
to_write: usize,
|
|
||||||
callback: Box<F>,
|
|
||||||
}
|
|
||||||
pub const SPEEDTEST_BYTES: usize = 64 * 1024 * 1024;
|
|
||||||
pub const SPEEDTEST_PATH: &str = "speedtest";
|
|
||||||
|
|
||||||
impl<F: Fn(f32)> AsyncRead for Speedtest<F> {
|
|
||||||
fn poll_read(
|
|
||||||
self: std::pin::Pin<&mut Self>,
|
|
||||||
_cx: &mut std::task::Context<'_>,
|
|
||||||
buf: &mut tokio::io::ReadBuf<'_>,
|
|
||||||
) -> std::task::Poll<std::io::Result<()>> {
|
|
||||||
let mut s = self;
|
|
||||||
let to_write = buf.remaining().min(s.to_write);
|
|
||||||
|
|
||||||
let filled = {
|
|
||||||
let fill_slice = buf.initialize_unfilled_to(to_write);
|
|
||||||
s.core.fill_bytes(fill_slice);
|
|
||||||
fill_slice.len()
|
|
||||||
};
|
|
||||||
s.to_write = s.to_write.saturating_sub(filled);
|
|
||||||
(s.callback)((1f32 - (s.to_write as f32 / SPEEDTEST_BYTES as f32)) * 100f32);
|
|
||||||
buf.advance(filled);
|
|
||||||
std::task::Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<F: Fn(f32)> Speedtest<F> {
|
|
||||||
pub fn new(callback: F) -> Self {
|
|
||||||
Self {
|
|
||||||
core: StdRng::from_rng(&mut rng()),
|
|
||||||
to_write: SPEEDTEST_BYTES,
|
|
||||||
callback: Box::new(callback),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
pub mod connect;
|
|
||||||
pub mod upload;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
cli::UploadInfo,
|
|
||||||
commands::connect::{config::Config, config_option::ConfigOption},
|
|
||||||
manifest::{ClosureFactory, CompressionOption, DepotManifest, generate_v2_manifest},
|
|
||||||
operator_builder::OperatorBuilder,
|
|
||||||
};
|
|
||||||
use futures::AsyncWriteExt;
|
|
||||||
use log::info;
|
|
||||||
use opendal::{FuturesAsyncWriter, Operator};
|
|
||||||
use tokio_util::compat::{Compat, FuturesAsyncWriteCompatExt};
|
|
||||||
|
|
||||||
pub async fn upload(
|
|
||||||
upload_info: &UploadInfo,
|
|
||||||
config: Config,
|
|
||||||
name: &Option<String>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let game_id = upload_info.game_id.clone();
|
|
||||||
let path = upload_info.path.clone();
|
|
||||||
let version_id = upload_info.version_id.clone();
|
|
||||||
|
|
||||||
let operator = get_operator(config, name)?;
|
|
||||||
|
|
||||||
let mut existing_depot_manifest = get_depot_manifest(&operator).await?;
|
|
||||||
|
|
||||||
info!("Uploading chunks");
|
|
||||||
|
|
||||||
let v2_manifest = generate_v2_manifest(
|
|
||||||
Path::new(&path),
|
|
||||||
ClosureFactory::new(
|
|
||||||
async move |id: String| {
|
|
||||||
info!("Uploading chunk id {id}");
|
|
||||||
let writer = operator
|
|
||||||
.writer(&format!("{game_id}/{version_id}/{id}"))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.into_futures_async_write()
|
|
||||||
.compat_write();
|
|
||||||
writer
|
|
||||||
},
|
|
||||||
|writer: Compat<FuturesAsyncWriter>| async {
|
|
||||||
writer.into_inner().close().await.unwrap();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
info!("Finished uploading chunks");
|
|
||||||
|
|
||||||
existing_depot_manifest.append(
|
|
||||||
upload_info.game_id.to_string(),
|
|
||||||
upload_info.version_id.to_string(),
|
|
||||||
CompressionOption::None,
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_depot_manifest(operator: &Operator) -> Result<DepotManifest, anyhow::Error> {
|
|
||||||
let existing_depot_manifest = operator.read("manifest.json").await?.to_bytes();
|
|
||||||
let existing_depot_manifest: DepotManifest =
|
|
||||||
serde_json::from_slice(existing_depot_manifest.as_ref())?;
|
|
||||||
Ok(existing_depot_manifest)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_operator(config: Config, name: &Option<String>) -> anyhow::Result<Operator> {
|
|
||||||
let operator = match if let Some(name) = name {
|
|
||||||
config
|
|
||||||
.get(name)
|
|
||||||
.ok_or(anyhow::anyhow!("Name does not exist"))?
|
|
||||||
} else {
|
|
||||||
config.get_active().ok_or(anyhow::anyhow!(
|
|
||||||
"No active connection set. Please specify with --name"
|
|
||||||
))?
|
|
||||||
} {
|
|
||||||
ConfigOption::S3(s3_config) => s3_config.build()?,
|
|
||||||
};
|
|
||||||
Ok(operator)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod interface;
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
use fern::colors::{Color, ColoredLevelConfig};
|
|
||||||
use log::LevelFilter;
|
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
pub fn configure_logging() -> anyhow::Result<()> {
|
|
||||||
let log_level = env::var("RUST_LOG")
|
|
||||||
.unwrap_or_else(|_| "info".to_string())
|
|
||||||
.parse::<LevelFilter>()?;
|
|
||||||
|
|
||||||
let log_dir = env::var("LOG_FILE_DIR").unwrap_or_else(|_| "logs".to_string());
|
|
||||||
|
|
||||||
fs::create_dir_all(&log_dir)?;
|
|
||||||
|
|
||||||
let colors = ColoredLevelConfig::new()
|
|
||||||
.error(Color::Red)
|
|
||||||
.warn(Color::Yellow)
|
|
||||||
.info(Color::Blue)
|
|
||||||
.debug(Color::Green)
|
|
||||||
.trace(Color::Magenta);
|
|
||||||
|
|
||||||
fern::Dispatch::new()
|
|
||||||
.chain(
|
|
||||||
fern::Dispatch::new()
|
|
||||||
.format(move |out, message, record| {
|
|
||||||
out.finish(format_args!(
|
|
||||||
"[{}] {}: {}",
|
|
||||||
chrono::Local::now().format("%H:%M:%S%.3f"),
|
|
||||||
colors.color(record.level()),
|
|
||||||
message
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.chain(io::stdout()),
|
|
||||||
)
|
|
||||||
.chain(
|
|
||||||
fern::Dispatch::new()
|
|
||||||
.format(|out, message, record| {
|
|
||||||
out.finish(format_args!(
|
|
||||||
"[{}] {} {} - {}",
|
|
||||||
chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"),
|
|
||||||
record.level(),
|
|
||||||
record.target(),
|
|
||||||
message
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.chain(fern::log_file(format!("{}/app.log", log_dir))?),
|
|
||||||
)
|
|
||||||
.level(log_level)
|
|
||||||
.apply()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#![feature(async_fn_traits)]
|
|
||||||
|
|
||||||
use crate::commands::connect::config::manage_configuration;
|
|
||||||
use crate::{
|
|
||||||
cli::{Cli, Commands},
|
|
||||||
commands::connect::config::Config,
|
|
||||||
commands::upload,
|
|
||||||
};
|
|
||||||
use clap::Parser;
|
|
||||||
mod cli;
|
|
||||||
mod commands;
|
|
||||||
mod logging;
|
|
||||||
mod manifest;
|
|
||||||
mod operator_builder;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> anyhow::Result<()> {
|
|
||||||
crate::logging::configure_logging()?;
|
|
||||||
|
|
||||||
let cli = Cli::parse();
|
|
||||||
|
|
||||||
let mut config = Config::read();
|
|
||||||
match cli.command {
|
|
||||||
Commands::Connect { name, option } => {
|
|
||||||
manage_configuration(&mut config, name, option).await?
|
|
||||||
}
|
|
||||||
Commands::Upload { info, name } => {
|
|
||||||
let info = info.interactive_configure();
|
|
||||||
upload::interface::upload(&info, config, &name).await?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
use std::{collections::HashMap, path::Path};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use droplet_rs::manifest::{Manifest, ManifestWriterFactory, generate_manifest_rusty};
|
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
use log::info;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio::io::AsyncWrite;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct DepotManifest {
|
|
||||||
content: HashMap<String, DepotManifestGameData>,
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct DepotManifestGameData {
|
|
||||||
version_id: String,
|
|
||||||
compression: CompressionOption,
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub enum CompressionOption {
|
|
||||||
None,
|
|
||||||
Gzip,
|
|
||||||
Zstd,
|
|
||||||
}
|
|
||||||
impl DepotManifest {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
content: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn append(&mut self, game_id: String, version_id: String, compression: CompressionOption) {
|
|
||||||
self.content.insert(
|
|
||||||
game_id,
|
|
||||||
DepotManifestGameData {
|
|
||||||
version_id,
|
|
||||||
compression,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClosureFactory<Writer, Factory, Closer>
|
|
||||||
where
|
|
||||||
Writer: AsyncWrite + Unpin,
|
|
||||||
Factory: AsyncFn(String) -> Writer,
|
|
||||||
Closer: AsyncFn(Writer),
|
|
||||||
{
|
|
||||||
writer: Factory,
|
|
||||||
closer: Closer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<
|
|
||||||
W: AsyncWrite + Unpin + Send + Sync,
|
|
||||||
F: AsyncFn(String) -> W + Send + Sync + 'static,
|
|
||||||
C: AsyncFn(W) + Send + Sync,
|
|
||||||
> ManifestWriterFactory for ClosureFactory<W, F, C>
|
|
||||||
where
|
|
||||||
for<'a> F::CallRefFuture<'a>: Send,
|
|
||||||
for<'b> C::CallRefFuture<'b>: Send,
|
|
||||||
{
|
|
||||||
type Writer = W;
|
|
||||||
|
|
||||||
async fn create(&self, id: String) -> anyhow::Result<Self::Writer> {
|
|
||||||
let func = &self.writer;
|
|
||||||
let output = func(id).await;
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
async fn close(&self, writer: Self::Writer) -> anyhow::Result<()> {
|
|
||||||
let func = &self.closer;
|
|
||||||
func(writer).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
W: AsyncWrite + Unpin + Send + Sync,
|
|
||||||
F: AsyncFn(String) -> W + Send + Sync + 'static,
|
|
||||||
C: AsyncFn(W) + Sync,
|
|
||||||
> ClosureFactory<W, F, C>
|
|
||||||
where
|
|
||||||
for<'a> F::CallRefFuture<'a>: Send,
|
|
||||||
for<'b> C::CallRefFuture<'b>: Send,
|
|
||||||
{
|
|
||||||
pub fn new(f: F, c: C) -> Self {
|
|
||||||
Self {
|
|
||||||
writer: f,
|
|
||||||
closer: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn generate_v2_manifest<Factory>(dir: &Path, factory: Factory) -> anyhow::Result<Manifest>
|
|
||||||
where
|
|
||||||
Factory: ManifestWriterFactory,
|
|
||||||
{
|
|
||||||
let progress_bar = ProgressBar::new(10_000).with_style(
|
|
||||||
ProgressStyle::default_bar()
|
|
||||||
.template("[{elapsed_precise}] [ETA {eta}] {bar} {percent_precise}%")
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
generate_manifest_rusty(
|
|
||||||
dir,
|
|
||||||
|progress| {
|
|
||||||
let progress_int = (progress * 100f32).round() as u64;
|
|
||||||
progress_bar.set_position(progress_int);
|
|
||||||
},
|
|
||||||
|log| progress_bar.suspend(|| info!("{}", log)),
|
|
||||||
Some(&factory),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
use opendal::Operator;
|
|
||||||
|
|
||||||
pub trait OperatorBuilder {
|
|
||||||
fn build(&self) -> anyhow::Result<Operator>;
|
|
||||||
}
|
|
||||||
@@ -53,17 +53,10 @@ import type { Component } from "vue";
|
|||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const navigation: Ref<
|
const navigation: (NavigationItem & { icon: Component; count?: number })[] = [
|
||||||
(NavigationItem & { icon: Component; count?: number })[]
|
{ label: t("home"), route: "/account", icon: HomeIcon, prefix: "/account" },
|
||||||
> = computed(() => [
|
|
||||||
{
|
{
|
||||||
label: t("account.home.title"),
|
label: t("security"),
|
||||||
route: "/account",
|
|
||||||
icon: HomeIcon,
|
|
||||||
prefix: "/account",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("account.security.title"),
|
|
||||||
route: "/account/security",
|
route: "/account/security",
|
||||||
prefix: "/account/security",
|
prefix: "/account/security",
|
||||||
icon: LockClosedIcon,
|
icon: LockClosedIcon,
|
||||||
@@ -74,12 +67,6 @@ const navigation: Ref<
|
|||||||
prefix: "/account/devices",
|
prefix: "/account/devices",
|
||||||
icon: DevicePhoneMobileIcon,
|
icon: DevicePhoneMobileIcon,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: t("account.token.title"),
|
|
||||||
route: "/account/tokens",
|
|
||||||
prefix: "/account/tokens",
|
|
||||||
icon: CodeBracketIcon,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t("account.notifications.notifications"),
|
label: t("account.notifications.notifications"),
|
||||||
route: "/account/notifications",
|
route: "/account/notifications",
|
||||||
@@ -87,13 +74,19 @@ const navigation: Ref<
|
|||||||
icon: BellIcon,
|
icon: BellIcon,
|
||||||
count: notifications.value.length,
|
count: notifications.value.length,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t("account.token.title"),
|
||||||
|
route: "/account/tokens",
|
||||||
|
prefix: "/account/tokens",
|
||||||
|
icon: CodeBracketIcon,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t("account.settings"),
|
label: t("account.settings"),
|
||||||
route: "/account/settings",
|
route: "/account/settings",
|
||||||
prefix: "/account/settings",
|
prefix: "/account/settings",
|
||||||
icon: WrenchScrewdriverIcon,
|
icon: WrenchScrewdriverIcon,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
const currentPageIndex = useCurrentNavigationIndex(navigation.value);
|
const currentPageIndex = useCurrentNavigationIndex(navigation);
|
||||||
</script>
|
</script>
|
||||||
@@ -4,14 +4,7 @@
|
|||||||
:href="`/auth/oidc?redirect=${route.query.redirect ?? '/'}`"
|
:href="`/auth/oidc?redirect=${route.query.redirect ?? '/'}`"
|
||||||
class="transition rounded-md grow inline-flex items-center justify-center bg-white/10 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-white/20"
|
class="transition rounded-md grow inline-flex items-center justify-center bg-white/10 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-white/20"
|
||||||
>
|
>
|
||||||
<i18n-t
|
<i18n-t keypath="auth.signin.externalProvider" tag="span" scope="global">
|
||||||
keypath="auth.signin.signinWithExternalProvider"
|
|
||||||
tag="span"
|
|
||||||
scope="global"
|
|
||||||
>
|
|
||||||
<template #externalProvider>{{
|
|
||||||
providerName || $t("auth.signin.externalProvider")
|
|
||||||
}}</template>
|
|
||||||
<template #arrow>
|
<template #arrow>
|
||||||
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
|
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -22,6 +15,4 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const { providerName = undefined } = defineProps<{ providerName?: string }>();
|
|
||||||
</script>
|
</script>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
v-model="username"
|
v-model="username"
|
||||||
name="username"
|
name="username"
|
||||||
type="username"
|
type="username"
|
||||||
autocomplete="username webauthn"
|
autocomplete="username"
|
||||||
required
|
required
|
||||||
class="block w-full rounded-md border-0 py-1.5 px-3 shadow-sm bg-zinc-950/20 text-zinc-300 ring-1 ring-inset ring-zinc-800 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
|
class="block w-full rounded-md border-0 py-1.5 px-3 shadow-sm bg-zinc-950/20 text-zinc-300 ring-1 ring-inset ring-zinc-800 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
|
||||||
/>
|
/>
|
||||||
@@ -86,78 +86,36 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { XCircleIcon } from "@heroicons/vue/20/solid";
|
import { XCircleIcon } from "@heroicons/vue/20/solid";
|
||||||
import {
|
import type { UserModel } from "~/prisma/client/models";
|
||||||
startAuthentication,
|
|
||||||
browserSupportsWebAuthn,
|
|
||||||
} from "@simplewebauthn/browser";
|
|
||||||
import { FetchError } from "ofetch";
|
|
||||||
|
|
||||||
const username = ref("");
|
const username = ref("");
|
||||||
const password = ref("");
|
const password = ref("");
|
||||||
const rememberMe = ref(false);
|
const rememberMe = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
async function passkeyAutofill() {
|
|
||||||
let silentWebauthnOptions;
|
|
||||||
try {
|
|
||||||
silentWebauthnOptions = await $dropFetch("/api/v1/auth/passkey/start", {
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await startAuthentication({
|
|
||||||
optionsJSON: silentWebauthnOptions,
|
|
||||||
useBrowserAutofill: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
|
|
||||||
await $dropFetch("/api/v1/auth/passkey/finish", {
|
|
||||||
method: "POST",
|
|
||||||
body: result,
|
|
||||||
});
|
|
||||||
|
|
||||||
await completeSignin();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
if (browserSupportsWebAuthn()) {
|
|
||||||
try {
|
|
||||||
await passkeyAutofill();
|
|
||||||
} catch (response) {
|
|
||||||
const message = (response as FetchError).message || t("errors.unknown");
|
|
||||||
error.value = message;
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const error = ref<string | undefined>();
|
const error = ref<string | undefined>();
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
async function signin_wrapper() {
|
function signin_wrapper() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
signin()
|
||||||
await signin();
|
.then(() => {
|
||||||
} catch (e) {
|
router.push(route.query.redirect?.toString() ?? "/");
|
||||||
if (e instanceof FetchError) {
|
})
|
||||||
error.value = e.data.message || t("errors.unknown");
|
.catch((response) => {
|
||||||
} else {
|
const message = response.statusMessage || t("errors.unknown");
|
||||||
error.value = e as string;
|
error.value = message;
|
||||||
}
|
})
|
||||||
} finally {
|
.finally(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signin() {
|
async function signin() {
|
||||||
const { result } = await $dropFetch("/api/v1/auth/signin/simple", {
|
await $dropFetch("/api/v1/auth/signin/simple", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
username: username.value,
|
username: username.value,
|
||||||
@@ -165,11 +123,7 @@ async function signin() {
|
|||||||
rememberMe: rememberMe.value,
|
rememberMe: rememberMe.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (result == "2fa") {
|
const user = useUser();
|
||||||
router.push({ query: route.query, path: "/auth/mfa" });
|
user.value = await $dropFetch<UserModel | null>("/api/v1/user");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await completeSignin();
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -10,18 +10,9 @@
|
|||||||
d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z"
|
d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<ApplicationLogo aria-hidden="true" class="h-6" />
|
<DropLogo aria-hidden="true" class="h-6" />
|
||||||
<span class="text-blue-400 font-display font-bold text-xl uppercase">
|
<span class="text-blue-400 font-display font-bold text-xl uppercase">
|
||||||
<template v-if="serverName">
|
{{ $t("drop.drop") }}
|
||||||
{{ serverName }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ $t("drop.drop") }}
|
|
||||||
</template>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
const { serverName } = await $dropFetch("/api/v1");
|
|
||||||
</script>
|
|
||||||
@@ -10,6 +10,6 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const url = computed(() => {
|
const url = computed(() => {
|
||||||
return `/api/v1/emoji/${twemoji.convert.toCodePoint(props.emoji)}`;
|
return `/twemoji/${twemoji.convert.toCodePoint(props.emoji)}.svg`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -44,9 +44,7 @@ const props = defineProps<{
|
|||||||
width?: number;
|
width?: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {
|
const { showGamePanelTextDecoration } = await $dropFetch(`/api/v1/settings`);
|
||||||
store: { showGamePanelTextDecoration },
|
|
||||||
} = await $dropFetch(`/api/v1/settings`);
|
|
||||||
|
|
||||||
const currentComponent = ref<HTMLDivElement>();
|
const currentComponent = ref<HTMLDivElement>();
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div v-if="game!">
|
<div v-if="game!">
|
||||||
<div class="grow flex flex-col xl:flex-row gap-y-8">
|
<div class="grow flex flex-row gap-y-8">
|
||||||
<div class="grow w-full h-full px-6 py-4 flex flex-col">
|
<div class="grow w-full h-full px-6 py-4 flex flex-col">
|
||||||
<div
|
<div
|
||||||
class="flex flex-col lg:flex-row lg:justify-between items-start lg:items-center gap-2"
|
class="flex flex-col lg:flex-row lg:justify-between items-start lg:items-center gap-2"
|
||||||
@@ -10,12 +10,10 @@
|
|||||||
<!-- icon image -->
|
<!-- icon image -->
|
||||||
<img :src="coreMetadataIconUrl" class="size-20" />
|
<img :src="coreMetadataIconUrl" class="size-20" />
|
||||||
<div>
|
<div>
|
||||||
<h1
|
<h1 class="text-5xl font-bold font-display text-zinc-100">
|
||||||
class="text-2xl xl:text-5xl font-bold font-display text-zinc-100"
|
|
||||||
>
|
|
||||||
{{ game.mName }}
|
{{ game.mName }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-1 text-sm xl:text-lg text-zinc-400">
|
<p class="mt-1 text-lg text-zinc-400">
|
||||||
{{ game.mShortDescription }}
|
{{ game.mShortDescription }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,11 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4 pt-8">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4 pt-8">
|
||||||
<SelectorMultiItem
|
<MultiItemSelector v-model="currentTags" :items="tags" />
|
||||||
v-model="currentTags"
|
|
||||||
:items="tags"
|
|
||||||
:create="createTag"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<label
|
<label
|
||||||
for="releaseDate"
|
for="releaseDate"
|
||||||
@@ -467,7 +461,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { GameModel } from "~/prisma/client/models";
|
import type { GameModel, GameTagModel } from "~/prisma/client/models";
|
||||||
import { micromark } from "micromark";
|
import { micromark } from "micromark";
|
||||||
import {
|
import {
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
@@ -477,7 +471,6 @@ import {
|
|||||||
} from "@heroicons/vue/24/solid";
|
} from "@heroicons/vue/24/solid";
|
||||||
import type { SerializeObject } from "nitropack";
|
import type { SerializeObject } from "nitropack";
|
||||||
import type { H3Error } from "h3";
|
import type { H3Error } from "h3";
|
||||||
import type { AdminFetchGameType } from "~/server/api/v1/admin/game/[id]/index.get";
|
|
||||||
|
|
||||||
const showUploadModal = ref(false);
|
const showUploadModal = ref(false);
|
||||||
const showAddCarouselModal = ref(false);
|
const showAddCarouselModal = ref(false);
|
||||||
@@ -485,9 +478,8 @@ const showAddImageDescriptionModal = ref(false);
|
|||||||
const showEditCoreMetadata = ref(false);
|
const showEditCoreMetadata = ref(false);
|
||||||
const mobileShowFinalDescription = ref(true);
|
const mobileShowFinalDescription = ref(true);
|
||||||
|
|
||||||
const game = defineModel<SerializeObject<AdminFetchGameType>>({
|
type ModelType = SerializeObject<GameModel & { tags: Array<GameTagModel> }>;
|
||||||
required: true,
|
const game = defineModel<ModelType>() as Ref<ModelType>;
|
||||||
});
|
|
||||||
if (!game.value)
|
if (!game.value)
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
@@ -497,9 +489,8 @@ if (!game.value)
|
|||||||
const currentTags = ref<{ [key: string]: boolean }>(
|
const currentTags = ref<{ [key: string]: boolean }>(
|
||||||
Object.fromEntries(game.value.tags.map((e) => [e.id, true])),
|
Object.fromEntries(game.value.tags.map((e) => [e.id, true])),
|
||||||
);
|
);
|
||||||
const rawTags = await $dropFetch("/api/v1/admin/tags");
|
const tags = (await $dropFetch("/api/v1/admin/tags")).map(
|
||||||
const tags = ref(
|
(e) => ({ name: e.name, param: e.id }) satisfies StoreSortOption,
|
||||||
rawTags.map((e) => ({ name: e.name, param: e.id }) satisfies StoreSortOption),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -510,11 +501,7 @@ watch(
|
|||||||
params: {
|
params: {
|
||||||
id: game.value.id,
|
id: game.value.id,
|
||||||
},
|
},
|
||||||
body: {
|
body: { tags: Object.keys(v) },
|
||||||
tags: Object.entries(v)
|
|
||||||
.filter((v) => v[1])
|
|
||||||
.map((v) => v[0]),
|
|
||||||
},
|
|
||||||
failTitle: "Failed to update game tags",
|
failTitle: "Failed to update game tags",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -825,15 +812,4 @@ async function updateImageCarousel() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createTag(value: string): Promise<string> {
|
|
||||||
const tag = await $dropFetch(`/api/v1/admin/tags`, {
|
|
||||||
method: "POST",
|
|
||||||
body: {
|
|
||||||
name: value,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
tags.value.push({ name: tag.name, param: tag.id });
|
|
||||||
return tag.id;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
|
<template>
|
||||||
|
<div v-if="game && unimportedVersions">
|
||||||
|
<div class="grow flex flex-row gap-y-8">
|
||||||
|
<div class="grow w-full h-full px-6 py-4 flex flex-col"></div>
|
||||||
|
<div
|
||||||
|
class="lg:overflow-y-auto lg:border-l lg:border-zinc-800 lg:block lg:inset-y-0 lg:z-50 lg:w-[30vw] flex flex-col gap-y-8 px-6 py-4"
|
||||||
|
>
|
||||||
|
<!-- version manager -->
|
||||||
|
<div>
|
||||||
|
<!-- version priority -->
|
||||||
|
<div>
|
||||||
|
<div class="border-b border-zinc-800 pb-3">
|
||||||
|
<div
|
||||||
|
class="flex flex-wrap items-center justify-between sm:flex-nowrap"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
class="text-base font-semibold font-display leading-6 text-zinc-100"
|
||||||
|
>
|
||||||
|
{{ $t("library.admin.versionPriority") }}
|
||||||
|
|
||||||
|
<!-- import games button -->
|
||||||
|
|
||||||
|
<NuxtLink
|
||||||
|
:href="canImport ? `/admin/library/${game.id}/import` : ''"
|
||||||
|
type="button"
|
||||||
|
:class="[
|
||||||
|
canImport
|
||||||
|
? 'bg-blue-600 hover:bg-blue-700'
|
||||||
|
: 'bg-blue-800/50',
|
||||||
|
'inline-flex w-fit items-center gap-x-2 rounded-md px-3 py-1 text-sm font-semibold font-display text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
canImport
|
||||||
|
? $t("library.admin.import.version.import")
|
||||||
|
: $t("library.admin.import.version.noVersions")
|
||||||
|
}}
|
||||||
|
</NuxtLink>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 text-center w-full text-sm text-zinc-600">
|
||||||
|
{{ $t("lowest") }}
|
||||||
|
</div>
|
||||||
|
<draggable
|
||||||
|
:list="game.versions"
|
||||||
|
handle=".handle"
|
||||||
|
class="mt-2 space-y-4"
|
||||||
|
@update="() => updateVersionOrder()"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
#item="{ element: item }: { element: GameVersionModelWithSize }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-full inline-flex items-center px-4 py-2 bg-zinc-800 rounded justify-between w-full flex"
|
||||||
|
>
|
||||||
|
<div class="text-zinc-100 font-semibold flex-none">
|
||||||
|
{{ item.versionName }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-right text-zinc-400 text-xs font-normal flex-auto pr-4"
|
||||||
|
>
|
||||||
|
{{ item.size && formatBytes(item.size) }}
|
||||||
|
</div>
|
||||||
|
<div class="text-zinc-400">
|
||||||
|
{{ item.delta ? $t("library.admin.version.delta") : "" }}
|
||||||
|
</div>
|
||||||
|
<div class="inline-flex items-center gap-x-2">
|
||||||
|
<component
|
||||||
|
:is="PLATFORM_ICONS[item.platform]"
|
||||||
|
class="size-6 text-blue-600"
|
||||||
|
/>
|
||||||
|
<Bars3Icon
|
||||||
|
class="cursor-move w-6 h-6 text-zinc-400 handle"
|
||||||
|
/>
|
||||||
|
<button @click="() => deleteVersion(item.versionName)">
|
||||||
|
<TrashIcon class="w-5 h-5 text-red-600" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
<div
|
||||||
|
v-if="game.versions.length == 0"
|
||||||
|
class="text-center font-bold text-zinc-400 my-3"
|
||||||
|
>
|
||||||
|
{{ $t("library.admin.version.noVersionsAdded") }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 text-center w-full text-sm text-zinc-600">
|
||||||
|
{{ $t("highest") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="grow w-full flex items-center justify-center">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<ExclamationCircleIcon
|
||||||
|
class="h-12 w-12 text-red-600"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<div class="mt-3 text-center sm:mt-5">
|
||||||
|
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
|
||||||
|
{{ $t("library.admin.offlineTitle") }}
|
||||||
|
</h1>
|
||||||
|
<div class="mt-4">
|
||||||
|
<p class="text-sm text-zinc-400 max-w-md">
|
||||||
|
{{ $t("library.admin.offline") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { GameModel, GameVersionModel } from "~/prisma/client/models";
|
||||||
|
import { Bars3Icon, TrashIcon } from "@heroicons/vue/24/solid";
|
||||||
|
import type { SerializeObject } from "nitropack";
|
||||||
|
import type { H3Error } from "h3";
|
||||||
|
import { ExclamationCircleIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { formatBytes } from "~/server/internal/utils/files";
|
||||||
|
|
||||||
|
// TODO implement UI for this page
|
||||||
|
|
||||||
|
const props = defineProps<{ unimportedVersions: string[] }>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const hasDeleted = ref(false);
|
||||||
|
|
||||||
|
const canImport = computed(
|
||||||
|
() => hasDeleted.value || props.unimportedVersions.length > 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
type GameVersionModelWithSize = GameVersionModel & { size: number };
|
||||||
|
|
||||||
|
type GameAndVersions = GameModel & {
|
||||||
|
versions: GameVersionModelWithSize[];
|
||||||
|
};
|
||||||
|
const game = defineModel<SerializeObject<GameAndVersions>>() as Ref<
|
||||||
|
SerializeObject<GameAndVersions>
|
||||||
|
>;
|
||||||
|
if (!game.value)
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: "Game not provided to editor component",
|
||||||
|
});
|
||||||
|
|
||||||
|
async function updateVersionOrder() {
|
||||||
|
try {
|
||||||
|
const newVersions = await $dropFetch("/api/v1/admin/game/version", {
|
||||||
|
method: "PATCH",
|
||||||
|
body: {
|
||||||
|
id: game.value.id,
|
||||||
|
versions: game.value.versions.map((e) => e.versionName),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
game.value.versions = newVersions;
|
||||||
|
} catch (e) {
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: t("errors.version.order.title"),
|
||||||
|
description: t("errors.version.order.desc", {
|
||||||
|
error: (e as H3Error)?.statusMessage ?? t("errors.unknown"),
|
||||||
|
}),
|
||||||
|
buttonText: t("common.close"),
|
||||||
|
},
|
||||||
|
(e, c) => c(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteVersion(versionName: string) {
|
||||||
|
try {
|
||||||
|
await $dropFetch("/api/v1/admin/game/version", {
|
||||||
|
method: "DELETE",
|
||||||
|
body: {
|
||||||
|
id: game.value.id,
|
||||||
|
versionName: versionName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
game.value.versions.splice(
|
||||||
|
game.value.versions.findIndex((e) => e.versionName === versionName),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
hasDeleted.value = true;
|
||||||
|
} catch (e) {
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: t("errors.version.delete.title"),
|
||||||
|
description: t("errors.version.delete.desc", {
|
||||||
|
error: (e as H3Error)?.statusMessage ?? t("errors.unknown"),
|
||||||
|
}),
|
||||||
|
buttonText: t("common.close"),
|
||||||
|
},
|
||||||
|
(e, c) => c(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
+2
-6
@@ -1,9 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-row items-center gap-x-2">
|
<div class="flex flex-row items-center gap-x-2">
|
||||||
<img
|
<img :src="game.icon" class="w-12 h-12 rounded-sm object-cover" />
|
||||||
:src="rawIcon ? game.icon : useObject(game.icon)"
|
|
||||||
class="w-12 h-12 rounded-sm object-cover"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col items-left">
|
<div class="flex flex-col items-left">
|
||||||
<h1 class="font-semibold font-display text-lg text-zinc-100">
|
<h1 class="font-semibold font-display text-lg text-zinc-100">
|
||||||
{{ game.name }}
|
{{ game.name }}
|
||||||
@@ -21,8 +18,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { GameMetadataSearchResult } from "~/server/internal/metadata/types";
|
import type { GameMetadataSearchResult } from "~/server/internal/metadata/types";
|
||||||
|
|
||||||
const { game, rawIcon = true } = defineProps<{
|
const { game } = defineProps<{
|
||||||
game: Omit<GameMetadataSearchResult, "year"> & { sourceName?: string };
|
game: Omit<GameMetadataSearchResult, "year"> & { sourceName?: string };
|
||||||
rawIcon?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<SelectorLanguageListbox />
|
<LanguageSelectorListbox />
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
class="mt-1 transition text-blue-500 hover:text-blue-600 text-sm"
|
class="mt-1 transition text-blue-500 hover:text-blue-600 text-sm"
|
||||||
to="https://translate.droposs.org/engage/drop/"
|
to="https://translate.droposs.org/engage/drop/"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<span class="text-xs font-mono text-zinc-400 inline-flex items-top gap-x-2"
|
<span class="text-xs font-mono text-zinc-400 inline-flex items-top gap-x-2"
|
||||||
><span v-if="!short" class="text-zinc-500">{{ log.time }}</span>
|
><span v-if="!short" class="text-zinc-500">{{ log.timestamp }}</span>
|
||||||
<span
|
<span
|
||||||
:class="[
|
:class="[
|
||||||
colours[log.level] || 'text-green-400',
|
colours[log.level] || 'text-green-400',
|
||||||
@@ -8,8 +8,9 @@
|
|||||||
]"
|
]"
|
||||||
>{{ log.level }}</span
|
>{{ log.level }}</span
|
||||||
>
|
>
|
||||||
<span v-if="log.prefix" class="text-zinc-200"> {{ log.prefix }}</span>
|
<pre :class="[short ? 'line-clamp-1' : '', 'mt-[1px]']">{{
|
||||||
<pre :class="[short ? 'line-clamp-1' : '', 'mt-[1px]']">{{ log.msg }}</pre>
|
log.message
|
||||||
|
}}</pre>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -11,7 +11,66 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<form @submit.prevent="() => addGame()">
|
<form @submit.prevent="() => addGame()">
|
||||||
<SelectorGame v-model="currentGame" :search="search" />
|
<Listbox v-model="currentGame" as="div">
|
||||||
|
<ListboxLabel
|
||||||
|
class="block text-sm font-medium leading-6 text-zinc-100"
|
||||||
|
>{{ $t("library.admin.import.selectGameSearch") }}</ListboxLabel
|
||||||
|
>
|
||||||
|
<div class="relative mt-2">
|
||||||
|
<ListboxButton
|
||||||
|
class="relative w-full cursor-default rounded-md bg-zinc-950 py-1.5 pl-3 pr-10 text-left text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-600 sm:text-sm sm:leading-6"
|
||||||
|
>
|
||||||
|
<GameSearchResultWidget
|
||||||
|
v-if="currentGame"
|
||||||
|
:game="currentGame"
|
||||||
|
/>
|
||||||
|
<span v-else class="block truncate text-zinc-600">
|
||||||
|
{{ $t("library.admin.import.selectGamePlaceholder") }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||||
|
>
|
||||||
|
<ChevronUpDownIcon
|
||||||
|
class="h-5 w-5 text-gray-400"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
|
<transition
|
||||||
|
leave-active-class="transition ease-in duration-100"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<ListboxOptions
|
||||||
|
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
|
||||||
|
>
|
||||||
|
<ListboxOption
|
||||||
|
v-for="result in metadataGames"
|
||||||
|
:key="result.id"
|
||||||
|
v-slot="{ active }"
|
||||||
|
as="template"
|
||||||
|
:value="result"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:class="[
|
||||||
|
active ? 'bg-blue-600 text-white' : 'text-zinc-100',
|
||||||
|
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<GameSearchResultWidget :game="result" />
|
||||||
|
</li>
|
||||||
|
</ListboxOption>
|
||||||
|
<p
|
||||||
|
v-if="metadataGames.length == 0"
|
||||||
|
class="w-full text-center p-2 uppercase font-display text-zinc-700 font-bold"
|
||||||
|
>
|
||||||
|
{{ $t("library.admin.metadata.companies.addGame.noGames") }}
|
||||||
|
</p>
|
||||||
|
</ListboxOptions>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
<div class="mt-6 flex items-center justify-between gap-3">
|
<div class="mt-6 flex items-center justify-between gap-3">
|
||||||
<label
|
<label
|
||||||
id="published-label"
|
id="published-label"
|
||||||
@@ -104,11 +163,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import type { GameModel } from "~/prisma/client/models";
|
import type { GameModel } from "~/prisma/client/models";
|
||||||
import { DialogTitle } from "@headlessui/vue";
|
import {
|
||||||
|
DialogTitle,
|
||||||
|
Listbox,
|
||||||
|
ListboxButton,
|
||||||
|
ListboxLabel,
|
||||||
|
ListboxOption,
|
||||||
|
ListboxOptions,
|
||||||
|
} from "@headlessui/vue";
|
||||||
|
import type { GameMetadataSearchResult } from "~/server/internal/metadata/types";
|
||||||
import { FetchError } from "ofetch";
|
import { FetchError } from "ofetch";
|
||||||
import type { SerializeObject } from "nitropack";
|
import type { SerializeObject } from "nitropack";
|
||||||
import { XCircleIcon } from "@heroicons/vue/24/solid";
|
import { XCircleIcon } from "@heroicons/vue/24/solid";
|
||||||
import type { GameMetadataSearchResult } from "~/server/internal/metadata/types";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
companyId: string;
|
companyId: string;
|
||||||
@@ -123,11 +189,26 @@ const emit = defineEmits<{
|
|||||||
];
|
];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const games = await $dropFetch("/api/v1/admin/game");
|
||||||
|
const metadataGames = computed(() =>
|
||||||
|
games
|
||||||
|
.filter((e) => !(props.exclude ?? []).includes(e.id))
|
||||||
|
.map(
|
||||||
|
(e) =>
|
||||||
|
({
|
||||||
|
id: e.id,
|
||||||
|
name: e.mName,
|
||||||
|
icon: useObject(e.mIconObjectId),
|
||||||
|
description: e.mShortDescription,
|
||||||
|
}) satisfies Omit<GameMetadataSearchResult, "year">,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const open = defineModel<boolean>({ required: true });
|
const open = defineModel<boolean>({ required: true });
|
||||||
|
|
||||||
const currentGame = ref<GameMetadataSearchResult>();
|
const currentGame = ref<(typeof metadataGames.value)[number]>();
|
||||||
const developed = ref(false);
|
const developed = ref(false);
|
||||||
const published = ref(false);
|
const published = ref(false);
|
||||||
const addGameLoading = ref(false);
|
const addGameLoading = ref(false);
|
||||||
@@ -162,10 +243,4 @@ async function addGame() {
|
|||||||
open.value = false;
|
open.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function search(query: string) {
|
|
||||||
return await $dropFetch("/api/v1/admin/search/game?type=Game", {
|
|
||||||
query: { q: query },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
+15
-2
@@ -59,6 +59,7 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const open = defineModel<boolean>({ required: true });
|
const open = defineModel<boolean>({ required: true });
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const collectionName = ref("");
|
const collectionName = ref("");
|
||||||
const createCollectionLoading = ref(false);
|
const createCollectionLoading = ref(false);
|
||||||
const collections = await useCollections();
|
const collections = await useCollections();
|
||||||
@@ -73,7 +74,6 @@ async function createCollection() {
|
|||||||
const response = await $dropFetch("/api/v1/collection", {
|
const response = await $dropFetch("/api/v1/collection", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { name: collectionName.value },
|
body: { name: collectionName.value },
|
||||||
failTitle: "Failed to create collection",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the game if provided
|
// Add the game if provided
|
||||||
@@ -83,7 +83,6 @@ async function createCollection() {
|
|||||||
>(`/api/v1/collection/${response.id}/entry`, {
|
>(`/api/v1/collection/${response.id}/entry`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { id: props.gameId },
|
body: { id: props.gameId },
|
||||||
failTitle: "Failed to add game to collection",
|
|
||||||
});
|
});
|
||||||
response.entries.push(entry);
|
response.entries.push(entry);
|
||||||
}
|
}
|
||||||
@@ -95,6 +94,20 @@ async function createCollection() {
|
|||||||
open.value = false;
|
open.value = false;
|
||||||
|
|
||||||
emit("created", response.id);
|
emit("created", response.id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create collection:", error);
|
||||||
|
|
||||||
|
const err = error as { statusMessage?: string };
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: t("errors.library.collection.create.title"),
|
||||||
|
description: t("errors.library.collection.create.desc", [
|
||||||
|
err?.statusMessage ?? t("errors.unknown"),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
(_, c) => c(),
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
createCollectionLoading.value = false;
|
createCollectionLoading.value = false;
|
||||||
}
|
}
|
||||||
@@ -119,10 +119,10 @@
|
|||||||
<div class="flex h-6 shrink-0 items-center">
|
<div class="flex h-6 shrink-0 items-center">
|
||||||
<div class="group grid size-4 grid-cols-1">
|
<div class="group grid size-4 grid-cols-1">
|
||||||
<input
|
<input
|
||||||
:id="`acl-${acl}`"
|
id="acl"
|
||||||
v-model="currentACLs[acl]"
|
v-model="currentACLs[acl]"
|
||||||
aria-describedby="acl-description"
|
aria-describedby="acl-description"
|
||||||
:name="`acl-${acl}`"
|
name="acl"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="col-start-1 row-start-1 appearance-none rounded-sm border checked:border-blue-600 checked:bg-blue-600 indeterminate:border-blue-600 indeterminate:bg-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:border-gray-300 disabled:bg-gray-100 disabled:checked:bg-gray-100 border-white/10 bg-white/5 dark:checked:border-blue-500 dark:checked:bg-blue-500 dark:indeterminate:border-blue-500 dark:indeterminate:bg-blue-500 dark:focus-visible:outline-blue-500 dark:disabled:border-white/5 dark:disabled:bg-white/10 dark:disabled:checked:bg-white/10 forced-colors:appearance-auto"
|
class="col-start-1 row-start-1 appearance-none rounded-sm border checked:border-blue-600 checked:bg-blue-600 indeterminate:border-blue-600 indeterminate:bg-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:border-gray-300 disabled:bg-gray-100 disabled:checked:bg-gray-100 border-white/10 bg-white/5 dark:checked:border-blue-500 dark:checked:bg-blue-500 dark:indeterminate:border-blue-500 dark:indeterminate:bg-blue-500 dark:focus-visible:outline-blue-500 dark:disabled:border-white/5 dark:disabled:bg-white/10 dark:disabled:checked:bg-white/10 forced-colors:appearance-auto"
|
||||||
/>
|
/>
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-sm/6">
|
<div class="text-sm/6">
|
||||||
<label
|
<label
|
||||||
:for="`acl-${acl}`"
|
for="acl"
|
||||||
class="font-display font-medium text-white"
|
class="font-display font-medium text-white"
|
||||||
>{{ acl }}</label
|
>{{ acl }}</label
|
||||||
>
|
>
|
||||||
+1
-1
@@ -22,7 +22,7 @@
|
|||||||
class="bg-red-600 text-white hover:bg-red-500"
|
class="bg-red-600 text-white hover:bg-red-500"
|
||||||
@click="() => deleteCollection()"
|
@click="() => deleteCollection()"
|
||||||
>
|
>
|
||||||
{{ $t("common.delete") }}
|
{{ $t("delete") }}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold font-display text-white hover:bg-zinc-700"
|
class="inline-flex items-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold font-display text-white hover:bg-zinc-700"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
class="bg-red-600 text-white hover:bg-red-500"
|
class="bg-red-600 text-white hover:bg-red-500"
|
||||||
@click="() => deleteArticle()"
|
@click="() => deleteArticle()"
|
||||||
>
|
>
|
||||||
{{ $t("common.delete") }}
|
{{ $t("delete") }}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold font-display text-white hover:bg-zinc-700"
|
class="inline-flex items-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold font-display text-white hover:bg-zinc-700"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
class="bg-red-600 text-white hover:bg-red-500"
|
class="bg-red-600 text-white hover:bg-red-500"
|
||||||
@click="() => deleteUser()"
|
@click="() => deleteUser()"
|
||||||
>
|
>
|
||||||
{{ $t("common.delete") }}
|
{{ $t("delete") }}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold font-display text-white hover:bg-zinc-700"
|
class="inline-flex items-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold font-display text-white hover:bg-zinc-700"
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="inline-flex gap-1 items-center flex-wrap">
|
||||||
|
<span
|
||||||
|
v-for="item in enabledItems"
|
||||||
|
:key="item.param"
|
||||||
|
class="inline-flex items-center gap-x-0.5 rounded-md bg-blue-600/10 px-2 py-1 text-xs font-medium text-blue-500 ring-1 ring-blue-800 ring-inset"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="group relative -mr-1 size-3.5 rounded-xs hover:bg-blue-600/20"
|
||||||
|
@click="() => remove(item.param)"
|
||||||
|
>
|
||||||
|
<span class="sr-only">{{ $t("common.remove") }}</span>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 14 14"
|
||||||
|
class="size-3.5 stroke-blue-500 group-hover:stroke-blue-400"
|
||||||
|
>
|
||||||
|
<path d="M4 4l6 6m0-6l-6 6" />
|
||||||
|
</svg>
|
||||||
|
<span class="absolute -inset-1" />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="enabledItems.length == 0"
|
||||||
|
class="font-display uppercase text-xs font-bold text-zinc-700"
|
||||||
|
>
|
||||||
|
{{ $t("common.noSelected") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Combobox as="div" @update:model-value="add">
|
||||||
|
<div class="relative mt-2">
|
||||||
|
<ComboboxInput
|
||||||
|
class="block w-full rounded-md bg-zinc-900 py-1.5 pr-12 pl-3 text-base text-zinc-100 outline-1 -outline-offset-1 outline-zinc-700 placeholder:text-zinc-500 focus:outline-2 focus:-outline-offset-2 focus:outline-blue-600 sm:text-sm/6"
|
||||||
|
:display-value="(item) => (item as StoreSortOption)?.name"
|
||||||
|
placeholder="Start typing..."
|
||||||
|
@change="search = $event.target.value"
|
||||||
|
@blur="search = ''"
|
||||||
|
/>
|
||||||
|
<ComboboxButton
|
||||||
|
class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-hidden"
|
||||||
|
>
|
||||||
|
<ChevronDownIcon class="size-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</ComboboxButton>
|
||||||
|
|
||||||
|
<ComboboxOptions
|
||||||
|
v-if="filteredItems.length > 0 || search.length > 0"
|
||||||
|
class="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-white/5 focus:outline-hidden sm:text-sm"
|
||||||
|
>
|
||||||
|
<ComboboxOption
|
||||||
|
v-for="item in filteredItems"
|
||||||
|
:key="item.param"
|
||||||
|
v-slot="{ active }"
|
||||||
|
:value="item.param"
|
||||||
|
as="template"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:class="[
|
||||||
|
'relative cursor-default py-2 pr-9 pl-3 select-none',
|
||||||
|
active
|
||||||
|
? 'bg-blue-600 text-white outline-hidden'
|
||||||
|
: 'text-zinc-100',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span class="block truncate">
|
||||||
|
{{ item.name }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
</ComboboxOptions>
|
||||||
|
</div>
|
||||||
|
</Combobox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import {
|
||||||
|
Combobox,
|
||||||
|
ComboboxButton,
|
||||||
|
ComboboxInput,
|
||||||
|
ComboboxOption,
|
||||||
|
ComboboxOptions,
|
||||||
|
} from "@headlessui/vue";
|
||||||
|
const props = defineProps<{
|
||||||
|
items: Array<StoreSortOption>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const model = defineModel<{ [key: string]: boolean }>();
|
||||||
|
|
||||||
|
const search = ref("");
|
||||||
|
const filteredItems = computed(() =>
|
||||||
|
props.items.filter(
|
||||||
|
(item) =>
|
||||||
|
!model.value?.[item.param] &&
|
||||||
|
item.name.toLowerCase().includes(search.value.toLowerCase()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const enabledItems = computed(() =>
|
||||||
|
props.items.filter((e) => model.value?.[e.param]),
|
||||||
|
);
|
||||||
|
|
||||||
|
function add(item: string) {
|
||||||
|
search.value = "";
|
||||||
|
model.value ??= {};
|
||||||
|
model.value[item] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(item: string) {
|
||||||
|
model.value ??= {};
|
||||||
|
model.value[item] = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
>
|
>
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
<!-- todo -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4 flex shrink-0">
|
<div class="ml-4 flex shrink-0">
|
||||||
@@ -43,12 +44,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { XMarkIcon } from "@heroicons/vue/24/solid";
|
import { XMarkIcon } from "@heroicons/vue/24/solid";
|
||||||
import type { SerializeObject } from "nitropack";
|
|
||||||
import type { NotificationModel } from "~/prisma/client/models";
|
import type { NotificationModel } from "~/prisma/client/models";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{ notification: NotificationModel }>();
|
||||||
notification: SerializeObject<NotificationModel>;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
async function deleteMe() {
|
async function deleteMe() {
|
||||||
await $dropFetch(`/api/v1/notifications/:id`, {
|
await $dropFetch(`/api/v1/notifications/:id`, {
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<h2 v-if="title" class="text-lg mb-4 w-full">{{ title }}</h2>
|
||||||
|
<div class="flex flex-col xl:flex-row gap-4">
|
||||||
|
<div class="relative flex grow max-w-[12rem]">
|
||||||
|
<svg class="aspect-square grow relative inline" viewBox="0 0 100 100">
|
||||||
|
<PieChartPieSlice
|
||||||
|
v-for="slice in slices"
|
||||||
|
:key="`${slice.percentage}-${slice.totalPercentage}`"
|
||||||
|
:slice="slice"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="absolute inset-0 bg-zinc-900 rounded-full m-12" />
|
||||||
|
</div>
|
||||||
|
<ul class="flex flex-col gap-y-1 justify-center text-left">
|
||||||
|
<li
|
||||||
|
v-for="slice in slices"
|
||||||
|
:key="slice.value"
|
||||||
|
class="text-sm inline-flex items-center gap-x-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="size-3 inline-block rounded-sm"
|
||||||
|
:class="CHART_COLOURS[slice.color].bg"
|
||||||
|
/>
|
||||||
|
{{
|
||||||
|
$t("common.labelValueColon", {
|
||||||
|
label: slice.label,
|
||||||
|
value: slice.value,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { generateSlices } from "~/components/PieChart/utils";
|
||||||
|
import type { SliceData } from "~/components/PieChart/types";
|
||||||
|
|
||||||
|
const { data, title = undefined } = defineProps<{
|
||||||
|
data: SliceData[];
|
||||||
|
title?: string | undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const slices = generateSlices(data);
|
||||||
|
</script>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user