Compare commits

..

460 Commits

Author SHA1 Message Date
DecDuck 4fb0a185f6 feat: preliminary peer api 2025-05-13 12:28:18 +10:00
DecDuck 70db79b50f feat: set data dir for headscale 2025-05-12 09:44:08 +10:00
Huskydog9988 ac7ef6303b feat: new unified data folder 2025-05-12 09:18:42 +10:00
DecDuck efbc86e73e feat: add new HeadscaleManager 2025-05-12 09:04:28 +10:00
DecDuck a0bc4bbc4c feat: refactored into separate metadata and library pages 2025-05-10 15:22:57 +10:00
DecDuck 90277653cb feat: rework developer/publisher system 2025-05-10 11:59:56 +10:00
DecDuck ac355918ed Merge branch 'Huskydog9988-further-improvements' into develop 2025-05-10 11:26:16 +10:00
DecDuck d6830c3428 feat: update search style 2025-05-10 11:25:45 +10:00
Huskydog9988 cbf480bef9 fix: pcgamingwiki not parsing company websites 2025-05-10 11:25:45 +10:00
Huskydog9988 afaaaf2eb5 feat: unified company metadata store
still need to migrate users from old developer and publisher tables
2025-05-10 11:25:45 +10:00
Huskydog9988 14f0833d17 feat: finish migrating to new objectid naming 2025-05-10 11:25:45 +10:00
Huskydog9988 7f7d8c8f45 fix: linting errors 2025-05-10 11:25:45 +10:00
Huskydog9988 52a7de0a8b feat: minimum support for unrefed object cleanup 2025-05-10 11:25:45 +10:00
Huskydog9988 dbded55113 feat: identify unused objects 2025-05-10 11:25:44 +10:00
Huskydog9988 aa3105aecd feat: manually edit search term for game import 2025-05-10 11:25:44 +10:00
Huskydog9988 0c48d42c2d fix: missing metadata preventing game import
when a metadata provider fails to import a game's developer / publisher, the import is no longer blocked. the imports usally fail because there isn't a page for these compaines
2025-05-10 11:25:44 +10:00
Huskydog9988 836ba33fe4 feat: basic screenshot manager 2025-05-10 11:25:44 +10:00
DecDuck 1f41e52a86 feat: update search style 2025-05-10 11:24:04 +10:00
Huskydog9988 17e4734cfb fix: pcgamingwiki not parsing company websites 2025-05-08 21:22:15 -04:00
Huskydog9988 b681476373 feat: unified company metadata store
still need to migrate users from old developer and publisher tables
2025-05-08 20:44:37 -04:00
DecDuck 3a9eb82fdf fix: fix invitation register url 2025-05-09 10:02:48 +10:00
Huskydog9988 7e5e7b032b feat: finish migrating to new objectid naming 2025-05-08 19:42:46 -04:00
Huskydog9988 df291c3e9a fix: linting errors 2025-05-08 19:26:46 -04:00
Huskydog9988 bf691a7f5c feat: minimum support for unrefed object cleanup 2025-05-08 19:20:34 -04:00
Huskydog9988 597a2264e8 feat: identify unused objects 2025-05-08 19:19:10 -04:00
Huskydog9988 1a2d3c8207 feat: manually edit search term for game import 2025-05-08 12:16:12 -04:00
Huskydog9988 471e85d7c6 fix: missing metadata preventing game import
when a metadata provider fails to import a game's developer / publisher, the import is no longer blocked. the imports usally fail because there isn't a page for these compaines
2025-05-08 11:57:37 -04:00
Huskydog9988 f9f437dd85 feat: basic screenshot manager 2025-05-08 11:38:09 -04:00
DecDuck 143846c48a fix: revert session store 2025-05-08 21:46:12 +10:00
DecDuck 2fb909f73d Revert "fix: convert socket sessions to cacheHandler"
This reverts commit 733aee3977.
2025-05-08 21:43:54 +10:00
DecDuck 0f773a9779 fix: docker build 2025-05-08 21:30:52 +10:00
DecDuck 92a98a5984 fix: add prisma binary targets 2025-05-08 20:21:50 +10:00
DecDuck 464af37afb chore: warn when metadata fails 2025-05-08 19:49:37 +10:00
DecDuck f8dc3fef55 fix: swap order of nuxt and prisma generations 2025-05-08 17:35:59 +10:00
DecDuck 6d35e2697d fix: switch prisma schema type 2025-05-08 17:33:07 +10:00
DecDuck 0d02be2392 fix: refactor prisma multifolder into docs example 2025-05-08 16:25:01 +10:00
DecDuck 48f796ae4b fix: update prisma schema and path 2025-05-08 16:20:19 +10:00
DecDuck 125fe9e6e2 fix: remove jank prisma script, and move to generated prisma client 2025-05-08 16:17:23 +10:00
DecDuck 29f3094ad4 Merge branch 'Huskydog9988-more-stuff' into develop 2025-05-08 15:50:47 +10:00
DecDuck 733aee3977 fix: convert socket sessions to cacheHandler 2025-05-08 15:50:29 +10:00
DecDuck e3ed60feae feat: add oidc to admin panel 2025-05-08 15:48:46 +10:00
DecDuck bfa2c0a641 feat: add oidc to admin panel 2025-05-08 15:29:50 +10:00
Huskydog9988 952df560ec chore: remove fsbackend console logs 2025-05-07 22:32:24 -04:00
Huskydog9988 1db2229ad3 fix: edgecase where object hash isn't read 2025-05-07 22:29:10 -04:00
Huskydog9988 731499be81 feat: unified cache handler 2025-05-07 22:26:34 -04:00
Huskydog9988 5aa0899bcf docs: why timeline was disabled 2025-05-07 21:45:13 -04:00
Huskydog9988 30b065dde3 fix: eslint error and server plugin name 2025-05-07 18:50:57 -04:00
Huskydog9988 1f510bee57 Merge branch 'develop' of https://github.com/Huskydog9988/drop into more-stuff 2025-05-07 18:45:36 -04:00
Huskydog9988 07b34c874d refactor: homepage link in header 2025-05-07 18:42:13 -04:00
DecDuck 19ff73cc30 feat: oidc 2025-05-07 22:14:04 +10:00
DecDuck e8633ceca2 fix: signout, again 2025-05-07 12:26:22 +10:00
DecDuck 770294d559 fix: ignore error if we are unable to blacklist certificate 2025-05-07 12:16:09 +10:00
DecDuck c0c55d35f4 fix: signout route 2025-05-07 11:33:21 +10:00
DecDuck a47debda91 fix: notification rendering 2025-05-07 09:54:02 +10:00
Huskydog9988 c449b45009 chore: remove redundant omitApi prisma option 2025-04-24 18:34:20 -04:00
Huskydog9988 f1f19c8263 feat: run tasks at startup 2025-04-23 21:14:16 -04:00
DecDuck 31ad8505b7 fix: task api and other small issues 2025-04-20 23:34:22 +10:00
DecDuck feedcfc5c4 fix: metadata init issues 2025-04-20 23:24:08 +10:00
DecDuck a5facbd648 fix: misc fixes 2025-04-20 21:33:19 +10:00
DecDuck 3b1d04251c feat: log metadata error if fail 2025-04-20 21:23:09 +10:00
DecDuck fd4ec3fd1c Update release.yml 2025-04-20 20:43:01 +10:00
DecDuck 0a270b267c fix: prisma migrations in docker
i hate prisma so so so so so much
2025-04-20 18:12:29 +10:00
DecDuck ec6d38d7af fix: copy package.json to Dockerfile 2025-04-19 21:51:44 +10:00
DecDuck a9d8ddc0f5 fix: prisma schema folder location 2025-04-19 21:51:22 +10:00
DecDuck dada379354 Merge pull request #37 from Huskydog9988/eslint
feat: add eslint and prettier
2025-04-19 11:40:21 +10:00
Huskydog9988 86c7aa33ce fix: don't set own script csp 2025-04-17 20:57:27 -04:00
Huskydog9988 26abb75e94 ci: add prefix to branch based docker tags 2025-04-17 20:18:21 -04:00
Huskydog9988 8eec8b19dd fix: userwidget not opening 2025-04-17 20:17:31 -04:00
Huskydog9988 582acfb385 fix: missing components errors 2025-04-16 12:03:42 -04:00
Huskydog9988 456902c784 fix: type error in $dropFetch 2025-04-15 22:01:24 -04:00
Huskydog9988 87215c4a1e chore: prettier pass 2025-04-15 21:46:34 -04:00
Huskydog9988 d361e01eef fix: last eslint errors 2025-04-15 21:43:27 -04:00
Huskydog9988 8e109dd562 fix: more eslint stuff 2025-04-15 21:10:45 -04:00
Huskydog9988 8f429e1e56 fix: eslint errors, switch to using maps 2025-04-15 20:04:45 -04:00
Huskydog9988 e362f732e7 fix: more eslint issues 2025-04-13 22:10:28 -04:00
Huskydog9988 86c2d00726 feat: add lint ci job 2025-04-13 21:46:41 -04:00
Huskydog9988 d4b89b5dc5 fix: inital eslint errors 2025-04-13 21:44:29 -04:00
Huskydog9988 ff1255f948 feat: add eslint and prettier 2025-04-13 21:43:35 -04:00
DecDuck 96742cc918 Merge branch 'Huskydog9988-more-ui-work' into develop 2025-04-14 10:54:09 +10:00
DecDuck c2bb835b0f fix: etags and other
remove sanitize-filename because IDs are internally generated
remove pulse animation on NO GAME cards
add migration
refactors to be inline with other stuff
2025-04-14 10:52:12 +10:00
Huskydog9988 f384492ed2 fix: missing key in loops 2025-04-12 16:03:35 -04:00
Huskydog9988 22a7cfa544 feat: save fs objectbackend hashes 2025-04-12 15:54:26 -04:00
Huskydog9988 228d109692 fix: incorrect perms when deleting objects 2025-04-11 19:32:30 -04:00
Huskydog9988 dc89ff95d8 feat: make internal objectbackend methods private 2025-04-10 19:57:08 -04:00
Huskydog9988 04c56fd985 chore: update @types/bcryptjs 2025-04-10 19:50:37 -04:00
Huskydog9988 ca03be7f43 fix: don't add write perms, users can only delete objects 2025-04-10 19:50:04 -04:00
Huskydog9988 35a2d98790 fix: don't use stream package 2025-04-10 19:29:11 -04:00
Huskydog9988 c4d8b24295 feat: hash objects for etag value 2025-04-09 14:48:13 -04:00
DecDuck 42349ad4e1 feat: allow client-based web tokens 2025-04-08 16:16:40 +10:00
Huskydog9988 e7566a6316 feat: upgrade to nuxt 3.16 2025-04-07 21:55:52 -04:00
Huskydog9988 fdffd9a32a feat: add skeleton for loading game carousel 2025-04-07 21:28:58 -04:00
Huskydog9988 4c3413ae63 feat: add nonce to scripts 2025-04-07 20:01:57 -04:00
Huskydog9988 30e3e7289a chore: drop unused dep 2025-04-07 19:57:47 -04:00
Huskydog9988 12ba416ca5 feat: enable nuxt dev tools 2025-04-07 19:57:00 -04:00
Huskydog9988 e4aeaee6e7 refactor: move tsconfig to nuxt config 2025-04-07 19:55:33 -04:00
Huskydog9988 9d943bc5dc feat: add etag to object response 2025-04-07 19:19:45 -04:00
Huskydog9988 66d1413eb5 fix: gamecarousel layout shifts (mostly) 2025-04-07 17:28:40 -04:00
Huskydog9988 e572b61af9 fix: gamecarousel not resizing 2025-04-07 16:57:44 -04:00
Huskydog9988 f9b774ddb5 fix: wordmark causing page refresh 2025-04-07 16:49:56 -04:00
Huskydog9988 106b3f66a4 feat: sleak transition from store page to item 2025-04-07 16:17:55 -04:00
Huskydog9988 657fd50702 fix: missing html attributes 2025-04-07 16:13:57 -04:00
Huskydog9988 7400fae11b fix: use nuxtlink instaed of a tag 2025-04-07 16:11:56 -04:00
DecDuck 043ef6dcd2 fix: remove debug values from game carousel 2025-04-07 17:44:48 +10:00
DecDuck 6ea50bffc8 fix: store page released date 2025-04-07 16:55:01 +10:00
DecDuck 9584d69e97 fix: add tsignore pending upstream fix 2025-04-07 10:52:56 +10:00
DecDuck 5ceff44993 Merge branch 'Huskydog9988-more-fixes' into develop 2025-04-07 10:40:20 +10:00
DecDuck 372a9bdd97 fix: devices page (reactivity and relative timestamps) 2025-04-07 10:37:01 +10:00
Huskydog9988 fe82c78571 refactor: remove momentjs 2025-04-06 19:44:38 -04:00
Huskydog9988 fd11d41dc5 chore: updates prisma and bycryptjs 2025-04-06 14:34:25 -04:00
Huskydog9988 9242a810b0 fix: don't prerender auth routes 2025-04-06 14:28:03 -04:00
Huskydog9988 0b9d0a4ad9 fix: don't use npm crypto 2025-04-06 14:27:31 -04:00
Huskydog9988 17d3e0ef54 refactor: use node crypto uuid 2025-04-06 14:08:36 -04:00
Huskydog9988 4fd2b159a6 fix: type error in devices page 2025-04-06 13:47:55 -04:00
Huskydog9988 d6d457f999 fix: generate prisma types on install (like nuxt) 2025-04-06 13:46:53 -04:00
DecDuck 54b3bc3a7e fix: devices page for mobile 2025-04-05 23:30:38 +11:00
DecDuck 2cbee3d495 feat: add ability to review and revoke clients 2025-04-05 17:42:32 +11:00
DecDuck 7263ec53ac fix: remove weird import 2025-04-05 15:38:07 +11:00
DecDuck 0edfdbdfce fix: return user library for client 2025-04-05 15:34:24 +11:00
DecDuck 114d235a6a fix: remove legacy metadata client routes 2025-04-05 15:34:12 +11:00
DecDuck a47615a274 refactor: move game id fetch to index 2025-04-05 15:33:59 +11:00
DecDuck 1987c578bc Merge branch 'Huskydog9988-better-ci' into develop 2025-04-05 14:22:38 +11:00
DecDuck b2327b21fe Merge branch 'better-ci' of https://github.com/Huskydog9988/drop into Huskydog9988-better-ci 2025-04-05 14:22:21 +11:00
DecDuck b22681c555 fix: fix types, remove @nuxt/image because of broken types 2025-04-05 14:21:37 +11:00
Huskydog9988 931913b836 ci: drop PAT_TOKEN 2025-04-04 19:19:33 -04:00
Huskydog9988 2be0e2f88c ci: add typecheck ci 2025-04-04 19:15:05 -04:00
Huskydog9988 7df175b747 ci: update actions versions 2025-04-04 19:03:38 -04:00
Huskydog9988 b6d05a6d09 ci: don't mark nightly container as latest 2025-04-04 18:59:18 -04:00
Huskydog9988 8d88728c99 feat: typecheck in dev 2025-04-04 18:52:34 -04:00
Huskydog9988 7141735664 fix: auth prerender 2025-04-04 18:51:01 -04:00
DecDuck 82baeb909a feat: add yarn typecheck and fix all types 2025-04-05 09:40:05 +11:00
DecDuck 2a85322f64 Merge branch 'Huskydog9988-db-store' into develop 2025-04-04 10:37:08 +11:00
DecDuck 088cb68604 chore: apply schema changes to db 2025-04-04 10:34:58 +11:00
Huskydog9988 81be7ccf58 Merge remote-tracking branch 'origin/develop' into db-store 2025-04-03 19:21:05 -04:00
Huskydog9988 a9d1a442f6 refactor: session handler 2025-04-03 19:15:33 -04:00
Huskydog9988 97043d6366 fix: register page validation 2025-04-03 19:11:35 -04:00
Huskydog9988 756bf8f93f fix: add missing dev deps 2025-04-03 18:15:39 -04:00
Huskydog9988 9dc35c80c5 Merge branch 'develop' into db-store 2025-04-03 18:12:07 -04:00
Huskydog9988 0f35d4a445 feat: cache for session store in db 2025-04-01 21:32:13 -04:00
DecDuck 57f50b0306 fix: renable nuxt-security with xss validator off 2025-04-01 21:20:54 +11:00
DecDuck 065951d91f fix: update last accessed client on push 2025-04-01 21:16:54 +11:00
DecDuck 36e6c92938 feat: add cloud save backend 2025-04-01 21:08:57 +11:00
DecDuck e7109e58bb feat: update readme 2025-04-01 18:36:51 +11:00
DecDuck 17372a9c06 feat: account pages framework & updates to library 2025-04-01 18:28:34 +11:00
DecDuck d7297707d7 fix: register redirect 2025-04-01 18:18:57 +11:00
DecDuck c809c8fcbf fix: re-request fix for $dropFetch 2025-04-01 16:58:53 +11:00
DecDuck 68f5f88347 fix: carousel pagination 2025-04-01 16:42:08 +11:00
DecDuck 6f8e28d711 fix: sidebar in library page 2025-04-01 16:19:03 +11:00
DecDuck 47dc364d4e Merge branch 'develop' of https://github.com/AdenMGB/drop into AdenMGB-develop 2025-04-01 12:11:57 +11:00
DecDuck 3b4f940983 feat: slight optimisation with removing from collection 2025-04-01 12:08:39 +11:00
DecDuck 1048653eef fix: $dropFetch SSR and rate limiting 2025-04-01 12:02:34 +11:00
DecDuck f1c932b7d7 fix: remove previous fix and add longer yarn install timeout 2025-03-31 12:35:34 +11:00
DecDuck 7c420ba7d7 fix: revert previous and add yarn clean/configs 2025-03-31 12:24:50 +11:00
DecDuck 7974361e5b fix: potential fix for timeout: switch to full image from slim 2025-03-31 12:18:14 +11:00
DecDuck 01171d788c fix: pin nuxt to 3.15.4 and recreate lockfile 2025-03-31 11:50:15 +11:00
DecDuck eec709a6e9 fix: recreate lockfile 2025-03-31 11:37:09 +11:00
DecDuck 5384759261 fix: update workflow to recurse submodules properly 2025-03-31 09:47:54 +11:00
DecDuck e3022bc52b fix: add submodules 2025-03-31 09:38:58 +11:00
DecDuck c7af02c15e fix: update dockerfile & use NPM droplet 2025-03-31 09:32:09 +11:00
DecDuck 96a1199fff fix: update dockerfile to debian 12 2025-03-27 11:09:37 +11:00
DecDuck 2cfc2cee7c Merge pull request #29 from Huskydog9988/patch-1
nightly builds and fix drop repo
2025-03-27 09:22:31 +11:00
Husky f5e52321b8 nightly builds and fix drop repo 2025-03-26 18:20:37 -04:00
DecDuck 58d558159d feat: github build 2025-03-27 09:16:22 +11:00
DecDuck e4e1c66bca fix: public assets 2025-03-24 12:55:07 +11:00
DecDuck 1996b97e99 refactor: use hash directly in authmek and version field on authmek 2025-03-24 12:50:21 +11:00
DecDuck cb4937b590 Merge pull request #28 from Huskydog9988/auth-overhaul
overhaul auth and futureproof it with argon2
2025-03-24 12:27:14 +11:00
Huskydog9988 57042892c4 move register to auth path 2025-03-23 21:08:08 -04:00
Huskydog9988 1f309606c9 address some issues 2025-03-23 21:07:12 -04:00
Huskydog9988 f9e6702d40 Merge branch 'develop' into auth-overhaul 2025-03-23 20:33:46 -04:00
Huskydog9988 690aa042df switch back to json 2025-03-23 20:29:50 -04:00
DecDuck 87f01a9984 Merge pull request #26 from Huskydog9988/perf-fixes
massive perf improvements
2025-03-23 10:56:35 +11:00
Huskydog9988 c1272dc7a7 use arktype for clientside validation 2025-03-22 19:37:28 -04:00
Huskydog9988 257cdacad4 make signup less error prone in db 2025-03-22 17:26:12 -04:00
Huskydog9988 2c9fdebf25 new accounts use argon2 2025-03-22 17:09:10 -04:00
Huskydog9988 2027c69c0e fix signout 2025-03-22 16:55:08 -04:00
Huskydog9988 e08a13f2c3 add sharp for ipx 2025-03-22 15:58:53 -04:00
Huskydog9988 6ed7e76b17 move auth pages into auth dir 2025-03-22 15:54:43 -04:00
DecDuck 573bf87cbb Merge pull request #27 from Huskydog9988/cleanup-platform-parse
fix: platformparse case sensitivity
2025-03-20 23:10:28 +00:00
Husky e8afa274a7 fix: platformparse case sensitivity 2025-03-20 17:52:04 -04:00
Husky d4d1eaf2e2 Merge branch 'develop' into perf-fixes 2025-03-16 12:16:47 -04:00
DecDuck 6918e78cf9 fix: macos and rate limiting 2025-03-15 15:04:52 +11:00
Husky cd93ba2197 massive perf improvements
on the store page, brings LCP down to only 5.6s from 11s. total lighthouse score is now 78
2025-03-14 20:18:26 -04:00
Aden Lindsay c052511ff3 fix(library): Use $dropFetch util 2025-03-15 09:54:20 +10:30
Aden Lindsay 19d1a9dd0e style(library): complete restyling to match client library 2025-03-15 09:46:15 +10:30
Aden Lindsay 66400f4875 fix(webui): Fix forcably redirecting to https 2025-03-15 09:45:09 +10:30
Huskydog9988 88a5dc2a58 add migrations to store ca and session in db 2025-03-14 11:37:31 -04:00
Huskydog9988 cf0af15854 store sessions in db 2025-03-14 11:35:12 -04:00
Huskydog9988 61764e81b8 store certs in db 2025-03-14 10:53:37 -04:00
DecDuck 98c8258127 fix?: https redirection issues 2025-03-14 20:31:00 +11:00
DecDuck 3527f678e5 fix: remove @nuxt/content 2025-03-14 13:23:56 +11:00
DecDuck fce084f95a Merge branch 'Huskydog9988-perf-improvements/fetch' into develop 2025-03-14 13:16:55 +11:00
DecDuck 1ad1ebb3fd merge: husky updates 2025-03-14 13:16:26 +11:00
DecDuck 1de9ebdfa5 feat: refactor news and migrate rest of useFetch to $dropFetch 2025-03-14 13:12:04 +11:00
DecDuck bd1cb67cd0 feat: user page & $dropFetch util 2025-03-14 12:22:08 +11:00
DecDuck 3225f536ce feat: lazy init igdb 2025-03-13 16:18:44 +11:00
DecDuck 58f91f4ac4 Merge branch 'Huskydog9988-metadata-improvements' into develop 2025-03-13 15:55:09 +11:00
DecDuck 8fc37936dc Merge branch 'metadata-improvements' of https://github.com/Huskydog9988/drop into Huskydog9988-metadata-improvements 2025-03-13 15:54:51 +11:00
DecDuck 0ca9a3b2f7 feat: database level metadata provider init 2025-03-13 15:20:13 +11:00
Huskydog9988 f8ae5b70c0 automate twitch credential refresh 2025-03-12 19:06:52 -04:00
Huskydog9988 7a3b30b012 accidently commited secret from testing lol (revoked) 2025-03-12 19:06:52 -04:00
Huskydog9988 4e8cffd778 make pcgamig wiki types match api return 2025-03-12 19:06:52 -04:00
Huskydog9988 bf7eb5b986 fix issue in igdb where company isn't found 2025-03-12 19:06:34 -04:00
Huskydog9988 77d06df7d3 fix manual metadata fetching publishers +types 2025-03-12 19:06:34 -04:00
Huskydog9988 2755aa472b in progress igdb 2025-03-12 19:06:34 -04:00
Huskydog9988 2b7bc6965d add content security policy to allow showing remote images for game importing 2025-03-12 19:05:11 -04:00
Huskydog9988 08164cae68 add pcgamingwiki as metadata source 2025-03-12 19:05:11 -04:00
Huskydog9988 2ca96c34f7 note that you need to init submodules 2025-03-12 19:04:32 -04:00
Huskydog9988 4b4e067fac cleanup giantbomb provider 2025-03-12 19:04:32 -04:00
Huskydog9988 d2b485456a gracefully disable metadata provider when api key is missing 2025-03-12 19:04:32 -04:00
quexeky 793b57a163 chore: Update changelog.md 2025-03-12 18:59:59 -04:00
quexeky d9218dea59 fix: Update README.md with discord link 2025-03-12 18:59:59 -04:00
DecDuck 789361ea73 feat: add mac as platform 2025-03-11 19:02:53 +11:00
DecDuck ffc1537d7f feat: partial work on home admin page 2025-03-11 18:25:29 +11:00
DecDuck 9d07070ef6 feat: add news client routes 2025-03-11 18:24:50 +11:00
DecDuck 0f0874c10a feat: file uploads on news articles 2025-03-11 17:51:46 +11:00
DecDuck 137241fdbb fix: notifications and store styling 2025-03-11 17:08:31 +11:00
DecDuck 9515a21dc6 feat: move article creation into a modal 2025-03-11 12:20:56 +11:00
DecDuck c3ee948682 fix: store page styling 2025-03-11 11:47:35 +11:00
DecDuck 9608cc9742 fix: re-enable metadata providers 2025-03-11 11:33:05 +11:00
DecDuck 88aaa2a71b fix: add aarch64 tag to runner 2025-03-11 09:06:41 +11:00
DecDuck 133503582a feat: client collection routes 2025-03-10 12:28:59 +11:00
DecDuck 1eede0f035 fix: news frontend 2025-03-10 12:05:10 +11:00
DecDuck b6f52f660a fix: unmerged changes 2025-03-10 11:42:33 +11:00
DecDuck a1f65b7e59 Merge branch 'AdenMGB-develop' into develop 2025-03-10 11:41:40 +11:00
DecDuck 1ce707788d fix: decduck's code review 2025-03-10 11:39:45 +11:00
DecDuck 31aaec74af feat: migrate to tailwind v4 and fix user token API 2025-03-10 10:35:03 +11:00
DecDuck 97792f0707 fix: home page now (temporarily) redirects to store 2025-03-10 10:34:57 +11:00
DecDuck b6189d12e7 fix(droplet): add aarch64 optional packages 2025-03-10 10:34:32 +11:00
DecDuck 0877638fc4 feat(acls): refactor & acl descriptions 2025-03-10 10:34:32 +11:00
DecDuck 090d2e6586 feat(acls): added backend acls 2025-03-10 10:34:26 +11:00
DecDuck a64a2479ba feat: migrate to tailwind v4 and fix user token API 2025-02-14 20:01:18 +11:00
DecDuck d8d5b938f0 fix: home page now (temporarily) redirects to store 2025-02-08 11:41:16 +11:00
DecDuck 3afd36a821 fix(droplet): add aarch64 optional packages 2025-02-08 11:38:06 +11:00
DecDuck ce8887528f feat(acls): refactor & acl descriptions 2025-02-07 17:26:23 +11:00
DecDuck d4dd259b5f feat(acls): added backend acls 2025-02-04 13:15:52 +11:00
Aden Lindsay 256fbd6afa fix(backend): Add forgotton migration for news storage 2025-02-03 16:50:10 +10:30
Aden Lindsay 9344d94e4c feat(api): Added API for deleting news articles 2025-02-02 10:21:43 +10:30
Aden Lindsay 1286248207 feat(api): Added API for retriving information about a spesific news article 2025-02-02 10:21:10 +10:30
Aden Lindsay 2ef8f2f93c feat(api): Added API for fetching news articles 2025-02-02 10:20:26 +10:30
Aden Lindsay 86053815f0 feat(api): Added API for creating articles 2025-02-02 10:19:31 +10:30
Aden Lindsay 88453f1ec4 feat(backend): Added backend communction between API & Frontend 2025-02-02 10:18:27 +10:30
Aden Lindsay 623ab7d786 feat(DB): Updated DB for news articles to be stored in the DB 2025-02-02 10:17:21 +10:30
Aden Lindsay 1ed15902a3 feat(news): Updated user for authoring articles 2025-02-02 10:16:28 +10:30
Aden Lindsay 3a55075532 feat(news): Created article full screen view 2025-02-02 10:15:09 +10:30
Aden Lindsay 6c7866ad14 feat(news): Created article overview page 2025-02-02 10:10:16 +10:30
Aden Lindsay f78b29b7fd feat(news) Added news page/sidebar 2025-02-02 10:09:25 +10:30
Aden Lindsay d8e964e06b feat(news): Added backend for news 2025-02-02 10:08:34 +10:30
Aden Lindsay 5d8f9d3813 Create useNews.ts 2025-02-02 10:07:24 +10:30
Aden Lindsay 28bf070ce2 feat(news): Added ability to search and filter news articles 2025-02-02 10:06:38 +10:30
Aden Lindsay 866c4d354e feat(news): Created ability to create news articles 2025-02-02 10:05:55 +10:30
Aden Lindsay e7837af0e7 feat(news): added ability to delete news articles 2025-02-02 10:05:13 +10:30
DecDuck 97b9b6dc11 Merge pull request #20 from AdenMGB/develop
Fresh animations & scaled Games Grid view properly
2025-01-30 19:30:21 +11:00
Aden Lindsay 09fd01d9b5 style(store): Added reactive hover state for admin dashboard button 2025-01-28 19:25:00 +10:30
Aden Lindsay 8fbe02a1b7 style(store): hover state for coursel button 2025-01-28 19:24:30 +10:30
Aden Lindsay dce116b66f style(collections): Hover state & scaling games grid properly 2025-01-28 19:23:24 +10:30
Aden Lindsay d167780562 fix(library): Fixed game banner as background, & added hover state 2025-01-28 19:22:29 +10:30
Aden Lindsay 6e057afb6d style(collections): Added hover state & animation for addition icon 2025-01-28 19:20:33 +10:30
Aden Lindsay 1967de72c8 style(GamePanel): New hover state styles 2025-01-28 19:17:12 +10:30
Aden Lindsay bfcc7519bf style(AddLibraryButton): New hover state animations 2025-01-28 19:15:49 +10:30
DecDuck 1a2aca9999 fix: collection overview trash icon 2025-01-28 17:23:06 +11:00
DecDuck 282e5bc2a6 fix: store page styling 2025-01-28 17:21:05 +11:00
DecDuck f369462e7f fix: cascade delete for collection entries 2025-01-28 17:06:30 +11:00
DecDuck 6317ad2657 fix: collection creation & overview hover styles 2025-01-28 17:01:34 +11:00
DecDuck 42ebbf2922 feat: collection deleting 2025-01-28 16:50:57 +11:00
DecDuck 7c1dec9401 fix: store style & library game page 2025-01-28 16:39:13 +11:00
DecDuck ecd26a42a8 feat: mobile ui and other design 2025-01-28 16:24:08 +11:00
DecDuck cf0aa948fe feat: refactor & redesign parts of UI 2025-01-28 16:24:08 +11:00
DecDuck 934c176974 refactor(create collection modal): use ModalTemplate & v-model 2025-01-28 16:24:08 +11:00
Aden Lindsay eea8f82bf9 squash: AdenMGB collection design & backend work
Update index.post.ts to implement saving collections functionality

Update index.get.ts to verify if collection exists and if user can access it

Update index.delete.ts to ask questions and not be so nonchalant

Update entry.post.ts

Update entry.delete.ts to do it better

Update index.vue to add functionality to the add to library button + fidgit with image

Update index.vue to also add add to library functionality, but no fidget :(

Update entry.post.ts to infact not remove it

Update index.ts

Update index.vue to manage collections from store page

Update index.ts to restrut for ahhhh

Update index.vue too add collection control to carosel

Update index.vue fix minor issue

Update index.vue to fix dropdown modal bug

Create library.vue for page layout

Create index.vue for library game details pane

Create index.vue for viewing collections pane

Create DeleteCollectionModal.vue component

Create CreateCollectionModal.vue component

Update AddLibraryButton.vue with dropdown :D

Update index.vue to use new components

Update index.vue for more components :O

Update entry.post.ts to not not return success, it'll figure it out

Update entry.delete.ts to not return...
2025-01-28 16:24:02 +11:00
Aden Lindsay 892f64fe3a Create signout.vue to sign out :shocked: 2025-01-28 16:19:41 +11:00
DecDuck 6bc3173d3a Merge branch 'backslash-fix' into develop 2025-01-28 15:20:30 +11:00
DecDuck 93a58c0d04 fix: import ui setup autocomplete 2025-01-28 15:20:06 +11:00
quexeky 3298b5f3ee Edit .gitlab-ci.yml 2025-01-26 12:21:38 +00:00
quexeky 6d03266ade Edit .gitlab-ci.yml 2025-01-26 12:17:19 +00:00
quexeky 1b3cf498f4 Edit .gitlab-ci.yml 2025-01-26 12:14:22 +00:00
quexeky 0bfe9803ac Edit .gitlab-ci.yml 2025-01-26 12:13:21 +00:00
quexeky 617278281e Edit .gitlab-ci.yml 2025-01-26 12:12:54 +00:00
quexeky 994db6c26a Edit .gitlab-ci.yml 2025-01-26 12:11:34 +00:00
DecDuck 36776cc61e fix(prisma): update prisma schemas 2025-01-19 16:46:43 +11:00
DecDuck a309651fe4 feat(collections): backend 2025-01-19 16:29:29 +11:00
DecDuck 716eac79bf cicd: custom dind 2025-01-14 20:43:50 +11:00
DecDuck f3ed0f6430 feat(tasks): improved ux 2025-01-14 20:43:22 +11:00
DecDuck c7eb11a836 fix(invitations): fix always being created as admin 2025-01-05 22:21:55 +11:00
DecDuck 39d7ce7d1b feat(import): overhauled version importing 2024-12-31 00:04:50 +11:00
DecDuck 02d6346b01 fix(utils): fix recursive dirs util 2024-12-31 00:04:14 +11:00
DecDuck a8f21068fc fix(tasks): fix disconnect code 2024-12-31 00:03:56 +11:00
DecDuck 2cfe75a551 feat(metadata): change name, description and icon 2024-12-30 14:17:27 +11:00
DecDuck 5a1f8411de feat(game panel): adjust gradient 2024-12-29 19:20:17 +11:00
DecDuck a86045c307 feat(carousel ui): add aden's carousel pagination design 2024-12-29 19:08:18 +11:00
DecDuck db103de24d fix(ci/cd): recurse submodules 2024-12-27 20:31:06 +11:00
DecDuck e505e58192 feat(admin ui): add modifying game descriptions 2024-12-27 19:52:59 +11:00
DecDuck b75ebd13b7 fix(drop-base): add drop-base as git submodule 2024-12-27 16:36:37 +11:00
DecDuck 937954fa02 feat(admin ui): customisable image carousel and new layout 2024-12-27 14:43:40 +11:00
DecDuck 43e32b44a2 fix(mail regex): removed global flag 2024-12-27 13:14:29 +11:00
DecDuck 61d88c3091 fix(callback ui): fix width of token breaking things 2024-12-27 13:07:29 +11:00
DecDuck 12e312593e feat(client ui): manual handshake 2024-12-27 13:07:29 +11:00
DecDuck 4f9b94921a feat(ui): add noWrapper option 2024-12-27 13:07:29 +11:00
quexeky 0a5a649cfe fix(account creation): Fix username length requirement
Signed-off-by: quexeky <git@quexeky.dev>
2024-12-27 08:17:03 +11:00
DecDuck 2f52a16d52 feat(metadata): 'manual' metadata provider 2024-12-26 19:54:49 +11:00
DecDuck 38fc6b81df feat(loading button): add disabled state 2024-12-26 19:38:48 +11:00
DecDuck 28baabc122 feat(store ui): 'no images' slide on image carousel 2024-12-26 19:37:44 +11:00
DecDuck 9b12d4576c feat(object transactions): support more types 2024-12-26 19:37:21 +11:00
DecDuck e5cf13fd93 feat(admin ui): mobile nav 2024-12-26 19:36:40 +11:00
DecDuck dbb315a8d7 fix(deploy template): fix FATAL: "root"... message 2024-12-26 12:34:10 +11:00
DecDuck 2c19e13c09 feat(admin ui): improve feedback when metadata fails 2024-12-25 23:36:39 +11:00
DecDuck fe9373af78 feat(version auto detection): add .sh for linux 2024-12-25 23:19:36 +11:00
quexeky eadcaa1b05 chore(changelog): Update changelog
Signed-off-by: quexeky <git@quexeky.dev>
2024-12-25 11:39:25 +11:00
quexeky 5f29c28e04 docs(version): Version bump
Signed-off-by: quexeky <git@quexeky.dev>
2024-12-25 11:19:31 +11:00
quexeky db916bf970 Update README.md 2024-12-24 23:24:13 +00:00
quexeky 2309407ca6 Update CONTRIBUTING.md 2024-12-24 21:34:05 +00:00
DecDuck 5c78b20504 fix(client event handler): future lenience 2024-12-24 17:46:56 +11:00
DecDuck cada630e81 fix: moved icons and created PlatformClient so we can use the enum on the frontend 2024-12-24 11:43:36 +11:00
DecDuck a361c38e82 fix(icons): remove unnecessary import 2024-12-24 10:56:30 +11:00
DecDuck 0f10626b1b fix(icons): remove dynamic imports 2024-12-24 10:49:04 +11:00
DecDuck 8945196633 fix(icons): properly import icons 2024-12-24 10:39:13 +11:00
DecDuck 31e8359ec0 chore(replace md-it): replaced markdown-it with micromark 2024-12-24 10:08:27 +11:00
DecDuck 089c3e03f6 feat(delete games): update prisma & delete games 2024-12-23 21:12:32 +11:00
DecDuck fd4a7d1981 feat(umu id override): add support for overriding UMU id 2024-12-23 13:33:22 +11:00
DecDuck b50e27f4b0 feat(library admin): client side search 2024-12-17 20:42:12 +11:00
DecDuck 54c5d55da7 fix(client chunk api route): follow best practices 2024-12-09 17:04:51 +11:00
DecDuck 25fc957092 fix(giantbomb): fix for missing developers or publishers 2024-12-08 16:07:42 +11:00
DecDuck 5393db3236 feat(download): add version metadata route 2024-12-07 11:00:56 +11:00
DecDuck 6df560ca37 fix(manifests): fix delta manifest generation 2024-12-06 22:16:42 +11:00
DecDuck 789d3ba2f2 refactor(windows logo): moved windows logo into logos dir 2024-12-06 21:25:21 +11:00
DecDuck c4391d3f4d style(invitation ui): non rounded bottom 2024-12-06 19:43:15 +11:00
DecDuck 1f4d07568f feat(client): fetch game endpoint 2024-11-26 20:11:25 +11:00
DecDuck e408ac5df8 feat(ui): more ui improvements 2024-11-24 16:12:19 +11:00
DecDuck 305de9f45a refactor: more consistent naming for globals 2024-11-24 15:29:14 +11:00
DecDuck ecc819eebc feat(ui): game carousel 2024-11-24 09:47:32 +11:00
DecDuck 9cbdcbcdf6 feat(store): new endpoints, ui and beginnings of main store page 2024-11-23 21:29:24 +11:00
DecDuck 9c2249ed08 fix(style): game panel now always shows 3 lines exactly 2024-11-23 09:12:57 +11:00
DecDuck 7b3b919581 feat(store page): add button to open in admin panel 2024-11-23 09:03:56 +11:00
DecDuck ef8f3ae6fd fix(library): only show versions that are directories 2024-11-23 08:49:38 +11:00
DecDuck 7a88f4c52d style: updated text colours across app 2024-11-19 11:11:59 +11:00
DecDuck 6e6f09dba0 feat(notifications): added notification system w/ interwoven refactoring 2024-11-16 19:41:19 +11:00
DecDuck 62ea9a116b fix(ws): fixed websocket authentication 2024-11-16 18:28:38 +11:00
DecDuck f7d767d73e feat(sessions): cleanup and raw accessors 2024-11-16 18:23:44 +11:00
DecDuck 26a31f6d56 feat(session): under the hood organisation and consolidation 2024-11-16 18:20:14 +11:00
DecDuck 5358f1f52c fix: properly disconnect websockets from task handler 2024-11-16 17:27:27 +11:00
DecDuck bc0c47c487 feat: new ws handler 2024-11-16 17:03:52 +11:00
DecDuck 8463e35a10 fix: admin invitation w/ system user 2024-11-16 17:03:04 +11:00
DecDuck 2859005ad4 fix: split prisma schemas 2024-11-16 16:30:22 +11:00
DecDuck 44c60280ef refactor: rename admin socket session map 2024-11-16 16:27:38 +11:00
DecDuck 9011cf5c83 refactor: split prisma schemas 2024-11-16 16:24:23 +11:00
DecDuck 2c21a235b2 feat: introduction of 'system user' 2024-11-16 16:18:15 +11:00
DecDuck 87230fb0e7 refactor: more consistent naming for object handler 2024-11-16 16:11:20 +11:00
DecDuck 4488ae269b feat(ui): smoother bar in admin task ui 2024-11-16 16:08:41 +11:00
DecDuck bfafe02d48 feat(ui): user widget now redirects to actual page 2024-11-16 16:08:23 +11:00
DecDuck d7160abc47 feat(registration): require lowercase usernames 2024-11-16 16:08:02 +11:00
DecDuck 76bceb121f feat: content length header for chunk downloads 2024-11-10 22:14:01 +11:00
DecDuck 952ece8c83 fix(client reg): prisma create footprint 2024-11-09 17:25:35 +11:00
DecDuck 33d37700e1 fix(admin library): results are returned alphabetically 2024-11-08 21:31:22 +11:00
DecDuck a815542604 feat(game page): more subtle design improvements 2024-11-08 21:23:31 +11:00
DecDuck e796b465d1 feat(game page): slightly improved game page 2024-11-08 20:59:17 +11:00
DecDuck b511b40d7c fix(invitations): fix types 2024-11-08 20:44:38 +11:00
DecDuck 599da0e348 feat(invitations): completed admin UI, with minor changes to backend 2024-11-07 23:23:49 +11:00
DecDuck c7b675f841 fix(invitations): fix expires requirement in the admin endpoint 2024-11-07 20:07:53 +11:00
DecDuck be6c30dfee chore(invitations): add expires field 2024-11-07 19:07:05 +11:00
DecDuck 7d72a86876 feat(client cap): client capability framework + peer API configuration 2024-11-06 12:25:00 +11:00
DecDuck adb4b7381e chore(utils): consolidate type utils 2024-11-06 11:49:08 +11:00
DecDuck f2e018277b feat(type utils): add enum dictionary type 2024-11-06 11:38:49 +11:00
DecDuck 9c4b6f35bb feat(ca): generate a server certificate for mtls APIs 2024-11-06 11:38:32 +11:00
DecDuck b9ae26cb27 chore: remove client API deadweight 2024-11-06 09:55:38 +11:00
DecDuck ce0a9ab538 chore: update prisma version 2024-11-06 09:29:28 +11:00
DecDuck 8999303f0a refactor(ca): change name of store file 2024-11-06 09:20:02 +11:00
DecDuck 69e4c2592b feat(client api): keep track of last connected 2024-11-06 09:17:03 +11:00
DecDuck 1d5e1bda85 chore(app settings): remove log statements 2024-11-05 19:03:47 +11:00
DecDuck 39fe9d55fd feat(library manager): automatically create library folder if it doesn't exist 2024-11-05 19:03:14 +11:00
DecDuck a8a152e578 feat(library ui): add header 2024-11-05 19:01:22 +11:00
DecDuck 74fa671b69 refactor(icons): move icons into dedicated folder 2024-11-05 18:39:49 +11:00
DecDuck 7b0756c6bd feat(registration): add title to tab 2024-11-05 13:18:50 +11:00
quexeky 17971e0a5a docs: Updated README.md
Removed non-existent Volunteer section in the TOC

Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 12:39:09 +11:00
quexeky d3d93b03de docs: Added SECURITY.md
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 12:39:09 +11:00
DecDuck 22366221b2 docs(readme): update information and setup guide 2024-11-05 12:22:49 +11:00
quexeky b0ef675e7e docs: Updated README.md
Removed some existing incorrect statements that had not been adapted fully. Still need to continue updating the contributing guide

Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 12:09:15 +11:00
quexeky cd0d2bfdea docs: updated CONTRIBUTING.md
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 12:04:46 +11:00
DecDuck 4273a20180 build only ci 2024-11-05 11:09:16 +11:00
DecDuck c4a419f2e4 update deploy template 2024-11-05 11:02:35 +11:00
DecDuck 3a51c9cc9c migrate bcrypt to bcryptjs 2024-11-05 10:56:34 +11:00
DecDuck c3914cc1ed remove bcrypt (debug) 2024-11-05 10:49:48 +11:00
DecDuck 69f341b329 Delete .gitlab-ci.yml 2024-11-04 22:29:47 +00:00
quexeky b03d790247 Added "Work in Progress" to CONTRIBUTING.md
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 09:29:09 +11:00
quexeky df41dd1c6f Relative link from README.md to CONTRIBUTING.md
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 09:27:19 +11:00
DecDuck ad25d3e462 fix registry authentication 2024-11-05 09:26:21 +11:00
quexeky 8097dd6721 Update styling
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 09:25:45 +11:00
DecDuck 803752e745 move to raw docker 2024-11-05 09:23:44 +11:00
DecDuck 6328c24537 copy autodevops configuration 2024-11-05 09:19:59 +11:00
quexeky f40f8c87f4 Updated image
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-05 09:16:19 +11:00
DecDuck 886beb62ea use autodevops build stage 2024-11-05 09:14:48 +11:00
DecDuck 0f80fcd830 disable tls in build 2024-11-05 09:11:30 +11:00
DecDuck 52315d09da use configuration from docs for ci/cd 2024-11-05 09:09:10 +11:00
DecDuck 62a111b0fc fixed docker daemon location 2024-11-05 09:07:26 +11:00
DecDuck 7194d35cf5 use gitlab ci variable declaration 2024-11-05 09:03:35 +11:00
DecDuck 03b0b0c38b manual ci/cd 2024-11-05 09:01:27 +11:00
DecDuck b744671e57 move to yarn v2 2024-11-04 22:55:29 +11:00
DecDuck a8f58eba7c remove canvas from dependencies 2024-11-04 22:38:05 +11:00
DecDuck 46d35adfdb back to yarn, with nuxt telemetry force disabled
I think Nuxt prompts have been messing with the installs
2024-11-04 22:15:36 +11:00
quexeky eb66c5c07a Resizing fix to icon?
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-04 22:00:39 +11:00
quexeky 13bfad4966 Forgot to remove a piece
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-04 21:59:39 +11:00
quexeky e47944a3b0 Some updates to README.md and CONTRIBUTING.md
Signed-off-by: quexeky <git@quexeky.dev>
2024-11-04 21:59:12 +11:00
DecDuck 9cb2d6d02f migrate to pnpm due to ci/cd issues with yarn 2024-11-04 21:52:31 +11:00
DecDuck 36568c3845 verbose yarn install 2024-11-04 21:31:32 +11:00
DecDuck a208fbedbd run yarn install in CI/CD non interactively 2024-11-04 21:21:32 +11:00
DecDuck 584bcf1cdf removed yarn.lock 2024-11-04 21:15:49 +11:00
DecDuck c5d00b4766 docker based deployment 2024-11-04 20:50:35 +11:00
DecDuck 5fe2036f0b immutable application settings framework 2024-11-02 11:32:36 +11:00
DecDuck 583301ff40 slight fixes to register logic 2024-10-31 20:28:29 +11:00
quexeky 88c95d6bf7 Updated tailwind config 2024-10-30 09:17:20 +11:00
DecDuck 848a611751 server side and user client side completed for registration 2024-10-28 22:16:29 +11:00
DecDuck 2e44ef3501 user mobile header 2024-10-27 15:52:30 +11:00
DecDuck ecb381e1ca break into single column store on lg devices 2024-10-27 15:30:26 +11:00
DecDuck b2ab827a55 add proper carousel to store page
uses the VueCarousel library to add an actual carousel to the store page
for the images. uses responsive styles
2024-10-27 15:21:31 +11:00
DecDuck 46551f9330 bump droplet and add vue carousel 2024-10-27 15:07:39 +11:00
DecDuck e4339c34ec Update file chunk.get.ts 2024-10-26 05:46:28 +00:00
DecDuck 7d2a1c6952 starting docs infra 2024-10-25 13:15:46 +11:00
DecDuck 24a0d118f2 slight bug fixes and clean up 2024-10-24 22:12:01 +11:00
DecDuck ef13b68592 better server side signin redirects
this makes it so if a user requests a page (not API route) and isn't
signed in, it automatically redirects them to the sign in page (doesn't
show a flash of the error page)
2024-10-23 12:55:38 +11:00
DecDuck c4a3e4e9a7 failed gracefully on invalid chunk index 2024-10-23 12:36:09 +11:00
DecDuck 7f4db0c1dc slight patch to rename query to be more consistent 2024-10-23 12:13:30 +11:00
DecDuck 3dd6062af4 added download chunk endpoint 2024-10-23 12:03:31 +11:00
DecDuck 6e2dc89670 Add LICENSE 2024-10-22 22:40:13 +00:00
DecDuck 93bc143dac additional polish and QoL features 2024-10-22 09:43:00 +11:00
DecDuck 03a37f72aa fixed manifest generation 2024-10-21 21:50:55 +11:00
DecDuck 7e176262cc more cleaning 2024-10-21 21:50:27 +11:00
DecDuck e1c1d7ea39 fixed task system 2024-10-21 21:50:21 +11:00
DecDuck c355f6fdbb cleanup & polish 2024-10-21 21:49:51 +11:00
DecDuck 0a715fef08 ability to fetch client certs for p2p 2024-10-21 10:14:13 +11:00
DecDuck 395219d0cb patch for no version check in manifest generation 2024-10-20 20:38:28 +11:00
DecDuck eb3f9f91ca check for no version in manifest generation 2024-10-20 20:31:07 +11:00
DecDuck cf578bd005 patch metadata handler 2024-10-20 19:44:00 +11:00
DecDuck 1f575b2bc0 small fixes & SSR disabled
SSR was causing weird issues where error pages would flash because the
user wasn't logged in. I'm disabling it for now, but I will eventually
look into ways to fix the issue and re-enable it.
2024-10-17 21:04:32 +11:00
DecDuck 91b7e1071c more client routes to support Drop app update 2024-10-15 20:05:38 +11:00
DecDuck 329c74d3ce game version re-ordering 2024-10-14 20:34:23 +11:00
DecDuck 8674ac7211 beginnings of download implementation 2024-10-12 17:34:09 +11:00
DecDuck 328b9ba46c fixes to store page for mobile clients 2024-10-12 17:33:31 +11:00
DecDuck 9b7ee4e746 upload images to games 2024-10-12 12:09:14 +11:00
DecDuck 27070b6a4c almst complete admin ui and initial store designs 2024-10-11 22:45:02 +11:00
DecDuck 46c8f0c48a version importing 2024-10-11 17:16:26 +11:00
DecDuck a7c33e7d43 completed game importing; partial work on version importing 2024-10-11 00:37:08 +11:00
DecDuck 718f5ba514 task API
The Task API allows for an easy way to create long-lived tasks that
require reporting back to user with progress/logs. It will be used in
the upcoming game importing.
2024-10-10 16:13:10 +11:00
DecDuck f3672f81dd patch signin 2024-10-10 13:39:43 +11:00
DecDuck 6b5e48d6fe admin ui shell 2024-10-09 15:43:55 +11:00
DecDuck 486bce8bc7 finished object endpoints
Added writing (tested) and deleting (untested) endpoints
2024-10-09 15:08:55 +11:00
DecDuck 435551c207 object storage + full permission system + testing
Object storage now works fully, with the permission system. It still
needs additional external endpoints for updating and deleting objects
from the API, but it is otherwise complete. Further tasks include
writing an S3 adapter.
2024-10-09 14:43:06 +11:00
DecDuck de388a937a object storage interface + utility functions
New ObjectBackend class that requires implementors to specify a few
basic functions, and it handles the permission logic on top of that.
Hopefully there is enough abstraction to suite further use cases!
2024-10-09 13:47:28 +11:00
DecDuck 4fa771a0b5 update dependencies and add note about optional dependencies 2024-10-09 02:35:59 +11:00
DecDuck 6ba5cdddc5 bump @drop/droplet version for windows developers 2024-10-09 02:26:21 +11:00
DecDuck d4e2dc8cb6 finalised client APIs and authentication method 2024-10-09 00:37:11 +11:00
DecDuck 425934d3ef Update README.md 2024-10-09 00:20:07 +11:00
DecDuck c4d81135a2 migrate to new droplet ca system 2024-10-08 21:45:44 +11:00
DecDuck 2b4382d013 handshakes 2024-10-08 18:08:34 +11:00
DecDuck 7523e536b5 another stage of client authentication 2024-10-08 16:13:46 +11:00
DecDuck 909432a6ce client initiate 2024-10-08 13:17:30 +11:00
DecDuck ceacd8469d README update 2024-10-08 00:56:15 +11:00
DecDuck 7869043c28 refactoring 2024-10-08 00:41:05 +11:00
DecDuck bfafd2a044 ca groundwork 2024-10-07 22:35:54 +11:00
DecDuck 1bd19ad917 more ui 2024-10-04 15:35:03 +10:00
DecDuck e52f072091 ui groundwork 2024-10-04 14:43:02 +10:00
DecDuck 22ac7f6b15 metadata engine 2024-10-04 13:01:06 +10:00
DecDuck 196f87c219 initial work on metadata system 2024-09-29 11:08:49 +10:00
DecDuck e1a789fa36 initial commit 2024-09-28 19:12:11 +10:00
1322 changed files with 16277 additions and 118912 deletions
+25 -5
View File
@@ -1,6 +1,26 @@
/sites
/cli
/desktop
/backend # go backend
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
node_modules
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example
.data
+4 -3
View File
@@ -1,7 +1,8 @@
DATABASE_URL="postgres://drop:drop@127.0.0.1:5432/drop"
CLIENT_CERTIFICATES="./.data/ca"
FS_BACKEND_PATH="./.data/objects"
GIANT_BOMB_API_KEY=""
EXTERNAL_URL="http://localhost:3000"
NUXT_PORT=4000
@@ -1,15 +1,6 @@
name: CI
on:
push:
branches:
- develop
pull_request:
branches:
- develop
permissions:
contents: read
on: [pull_request, push]
jobs:
typecheck:
@@ -21,20 +12,17 @@ jobs:
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: "pnpm"
cache: "yarn"
- name: Install dependencies
run: pnpm install
run: yarn install --immutable --network-timeout 1000000
- name: Typecheck
run: pnpm run typecheck
run: yarn typecheck
lint:
name: Lint
@@ -45,17 +33,14 @@ jobs:
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: "pnpm"
cache: "yarn"
- name: Install dependencies
run: pnpm install
run: yarn install --immutable --network-timeout 1000000
- name: Lint
run: pnpm run lint
run: yarn lint
-132
View File
@@ -1,132 +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
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 }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
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'
+68
View File
@@ -0,0 +1,68 @@
name: Release Workflow
on:
workflow_dispatch: {}
release:
types: [published]
# This can be used to automatically publish nightlies at UTC nighttime
schedule:
- cron: "0 2 * * *" # run at 2 AM UTC
jobs:
web:
name: Push website Docker image to registry
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-flags: --debug
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/drop-OSS/drop
tags: |
type=schedule,pattern=nightly
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=ref,event=branch,prefix=branch-
type=ref,event=pr
type=sha
# set latest tag for stable releases
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.release.prerelease == false }}
- name: Build and push image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
push: 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
-159
View File
@@ -1,159 +0,0 @@
name: Build and release server
on:
workflow_dispatch: {}
release:
types: [published]
# This can be used to automatically publish nightlies at UTC nighttime
schedule:
- cron: "0 2 * * *" # run at 2 AM UTC
env:
REGISTRY_IMAGE: ghcr.io/drop-oss/drop
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
fetch-depth: 3 # fix for when this gets triggered by tag
fetch-tags: true
ref: ${{ github.ref }}
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
id: get_final_ver
run: |
BASE_VER=v$(jq -r '.version' package.json)
TODAY=$(date +'%Y.%m.%d')
echo "Today will be: $TODAY"
echo "today=$TODAY" >> $GITHUB_OUTPUT
if [[ "${{ github.event_name }}" == "release" ]]; then
FINAL_VER="$BASE_VER"
else
FINAL_VER="${BASE_VER}-nightly.$TODAY"
fi
echo "Drop's release tag will be: $FINAL_VER"
echo "final_ver=$FINAL_VER" >> $GITHUB_OUTPUT
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
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 }}
context: ./server
file: ./server/Dockerfile
- name: Export digest
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
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/drop-OSS/drop
tags: |
type=schedule,pattern=nightly
type=schedule,pattern=nightly.${{ steps.get_final_ver.outputs.today }}
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=ref,event=branch,prefix=branch-
type=ref,event=pr
type=sha
# set latest tag for stable releases
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.release.prerelease == false }}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
+36 -2
View File
@@ -1,2 +1,36 @@
dist/
node_modules/
# Nuxt dev/build outputs
.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
+54
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
[submodule "drop-base"]
path = drop-base
url = https://github.com/Drop-OSS/drop-base.git
+1
View File
@@ -0,0 +1 @@
drop-base/
+21
View File
@@ -0,0 +1,21 @@
{
"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"
"editor.quickSuggestions": {
"strings": "on"
},
// prioritize ArkType's "type" for autoimports
"typescript.preferences.autoImportSpecifierExcludeRegexes": ["^(node:)?os$"]
}
+242
View File
@@ -0,0 +1,242 @@
# CONTRIBUTING GUIDELINES
Drop is a community-driven project. Contribution is welcome, encouraged, and appreciated.
It is also essential for the development of the project.
First, please take a moment to review our [code of conduct](CODE_OF_CONDUCT.md).
These guidelines are an attempt at better addressing pending
issues and pull requests. Please read them closely.
Foremost, be so kind as to [search](#use-the-search-luke). This ensures any contribution
you would make is not already covered.
<!-- TOC updateonsave:true depthfrom:2 -->
- [Reporting Issues](#reporting-issues)
- [You have a problem](#you-have-a-problem)
- [You have a suggestion](#you-have-a-suggestion)
- [Submitting Pull Requests](#submitting-pull-requests)
- [Getting started](#getting-started)
- [You have a solution](#you-have-a-solution)
- [You have an addition](#you-have-an-addition)
- [Use the Search, Luke](#use-the-search-luke)
- [Commit Guidelines](#commit-guidelines)
- [Format](#format)
- [Style](#style)
<!-- /TOC -->
## Reporting Issues
### You have a problem
Please be so kind as to [search](#use-the-search-luke) for any open issue already covering
your problem.
If you find one, comment on it, so we know more people are experiencing it.
<!--
TODO: Add Troubleshooting
If not, look at the [Troubleshooting](https://github.com/Drop-OSS/docs/Troubleshooting)
page for instructions on how to gather data to better debug your problem.
-->
If you cannot find an existing issue, you can go ahead and create an issue with as much
detail as you can provide.
It should include the data gathered as indicated above, along with the following:
1. How to reproduce the problem
2. What the correct behavior should be
3. What the actual behavior is
Please copy to anyone relevant (e.g. plugin maintainers) by mentioning their GitHub handle
(starting with `@`) in your message.
We will do our very best to help you.
### You have a suggestion
Please be so kind as to [search](#use-the-search-luke) for any open issue already covering
your suggestion.
If you find one, comment on it, so we know more people are supporting it.
If not, you can go ahead and create an issue. Please copy to anyone relevant (e.g. plugin
maintainers) by mentioning their GitHub handle (starting with `@`) in your message.
## Submitting Pull Requests
### Getting started
You should be familiar with the basics of
[contributing on GitHub](https://help.github.com/articles/using-pull-requests)
<!--and have a fork
[properly set up](https://github.com/drop/docs/Contribution-Technical-Practices).
-->
You MUST always create PRs with _a dedicated branch_ based on the latest upstream tree.
If you create your own PR, please make sure you do it right. Also be so kind as to reference
any issue that would be solved in the PR description body,
[for instance](https://help.github.com/articles/closing-issues-via-commit-messages/)
_"Fixes #XXXX"_ for issue number XXXX.
### You have a solution
Please be so kind as to [search](#use-the-search-luke) for any open issue already covering
your [problem](#you-have-a-problem), and any pending/merged/rejected PR covering your solution.
If the solution is already reported, try it out and +1 the pull request if the
solution works ok. On the other hand, if you think your solution is better, post
it with reference to the other one so we can have both solutions to compare.
If not, then go ahead and submit a PR. Please copy to anyone relevant (e.g. plugin
maintainers) by mentioning their GitHub handle (starting with `@`) in your message.
### You have an addition
We are absolutely accepting more contributions or features to drop, but please, make sure
that it is reasonable. Contributions that only cover a very small niche are likely to not
be added.
Please be so kind as to [search](#use-the-search-luke) for any pending, merged or rejected Pull Requests
covering or related to what you want to add.
If you find one, try it out and work with the author on a common solution.
If not, then go ahead and submit a PR. Please copy to anyone relevant (e.g. plugin
maintainers) by mentioning their GitHub handle (starting with `@`) in your message.
For any extensive change, such as API changes, you will have to find testers to +1 your PR.
---
## Use the Search, Luke
_May the Force (of past experiences) be with you_
GitHub offers [many search features](https://help.github.com/articles/searching-github/)
to help you check whether a similar contribution to yours already exists. Please search
before making any contribution, it avoids duplicates and eases maintenance. Trust me,
that works 90% of the time.
You can also take a look at the [FAQ](https://github.com/Drop-OSS/docs/wiki/FAQ)
to be sure your contribution has not already come up.
If all fails, your thing has probably not been reported yet, so you can go ahead
and [create an issue](#reporting-issues) or [submit a PR](#submitting-pull-requests).
---
## Commit Guidelines
Drop uses the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
specification. The automatic changelog tool uses these to automatically generate
a changelog based on the commit messages. Here's a guide to writing a commit message
to allow this:
### Format
```
type(scope)!: subject
```
- `type`: the type of the commit is one of the following:
- `feat`: new features.
- `fix`: bug fixes.
- `docs`: documentation changes.
- `refactor`: refactor of a particular code section without introducing
new features or bug fixes.
- `style`: code style improvements.
- `perf`: performance improvements.
- `test`: changes to the test suite.
- `ci`: changes to the CI system.
- `build`: changes to the build system.
- `chore`: for other changes that don't match previous types. This doesn't appear
in the changelog.
- `scope`: section of the codebase that the commit makes changes to. If it makes changes to
many sections, or if no section in particular is modified, leave blank without the parentheses.
Examples:
- Commit that changes the `git` plugin:
```
feat(git): add alias for `git commit`
```
- Commit that changes many plugins:
```
style: fix inline declaration of arrays
```
For changes to plugins or themes, the scope should be the plugin or theme name:
- ✅ `fix(agnoster): commit subject`
- ❌ `fix(theme/agnoster): commit subject`
- `!`: this goes after the `scope` (or the `type` if scope is empty), to indicate that the commit
introduces breaking changes.
Optionally, you can specify a message that the changelog tool will display to the user to indicate
what's changed and what they can do to deal with it. You can use multiple lines to type this message;
the changelog parser will keep reading until the end of the commit message or until it finds an empty
line.
Example (made up):
```
style(agnoster)!: change dirty git repo glyph
BREAKING CHANGE: the glyph to indicate when a git repository is dirty has
changed from a Powerline character to a standard UTF-8 emoji. You can
change it back by setting `ZSH_THEME_DIRTY_GLYPH`.
Fixes #420
Co-authored-by: Username <email>
```
- `subject`: a brief description of the changes. This will be displayed in the changelog. If you need
to specify other details, you can use the commit body, but it won't be visible.
Formatting tricks: the commit subject may contain:
- Links to related issues or PRs by writing `#issue`. This will be highlighted by the changelog tool:
```
feat(archlinux): add support for aura AUR helper (#9467)
```
- Formatted inline code by using backticks: the text between backticks will also be highlighted by
the changelog tool:
```
feat(shell-proxy): enable unexported `DEFAULT_PROXY` setting (#9774)
```
### Style
Try to keep the first commit line short. It's harder to do using this commit style but try to be
concise, and if you need more space, you can use the commit body. Try to make sure that the commit
subject is clear and precise enough that users will know what changed by just looking at the changelog.
---
<!--
## Volunteer
Very nice!! :)
Please have a look at the [Volunteer](https://github.com/ohmyzsh/ohmyzsh/wiki/Volunteers)
page for instructions on where to start and more.
-->
## Reference
This contributing guide is adapted from the
[oh-my-zsh contribution guide](https://github.com/ohmyzsh/ohmyzsh/blob/master/CONTRIBUTING.md).
If there are any issues with this, please email admin@deepcore.dev.
+20 -66
View File
@@ -1,76 +1,30 @@
# syntax=docker/dockerfile:1
# pull pre-configured and updated build environment
FROM debian:testing-20250317-slim AS build-system
FROM node:lts-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
# setup workdir - has to be the same filepath as app because fuckin' Prisma
WORKDIR /app
## so corepack knows pnpm's version
# install dependencies and build
RUN apt-get update -y
RUN apt-get install node-corepack -y
RUN corepack enable
COPY . .
## prevent prompt to download
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
## setup for offline
RUN corepack pack
## don't call out to network anymore
ENV COREPACK_ENABLE_NETWORK=0
### INSTALL DEPS ONCE
FROM base AS deps
RUN pnpm install --frozen-lockfile --ignore-scripts
### BUILD TORRENTIAL
FROM rustlang/rust:nightly-alpine AS torrential-build
RUN apk add musl-dev pkgconfig libarchive-dev libarchive
WORKDIR /build
COPY . .
RUN apk add protoc
RUN cargo build --release --manifest-path ./torrential/Cargo.toml
### BUILD APP
FROM base AS build-system
ENV NODE_ENV=production
ENV NUXT_TELEMETRY_DISABLED=1
## add git so drop can determine its git ref at build
RUN apk add --no-cache git
## copy deps and rest of project files
COPY . .
COPY --from=deps /app/node_modules ./node_modules
ARG BUILD_DROP_VERSION
ARG BUILD_GIT_REF
## build
RUN pnpm run --filter=drop postinstall && pnpm run --filter=drop build
RUN NUXT_TELEMETRY_DISABLED=1 yarn install --network-timeout 1000000
RUN NUXT_TELEMETRY_DISABLED=1 yarn prisma generate
RUN NUXT_TELEMETRY_DISABLED=1 yarn build
# create run environment for Drop
FROM base AS run-system
FROM node:lts-slim AS run-system
ENV NODE_ENV=production
ENV NUXT_TELEMETRY_DISABLED=1
WORKDIR /app
# 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 apk add --no-cache pnpm 7zip nginx
RUN pnpm install prisma@7.3.0 --global
# init prisma to download all required files
RUN pnpm prisma init
COPY --from=build-system /app/.output ./app
COPY --from=build-system /app/prisma ./prisma
COPY --from=build-system /app/package.json ./
COPY --from=build-system /app/build ./startup
COPY --from=build-system /app/server/prisma.config.ts ./
COPY --from=build-system /app/server/.output ./app
COPY --from=build-system /app/server/prisma ./prisma
COPY --from=build-system /app/server/build ./startup
COPY --from=build-system /app/server/build/nginx.conf /nginx.conf
COPY --from=torrential-build /build/torrential/target/release/torrential /usr/bin/
# OpenSSL as a dependency for Drop (TODO: seperate build environment)
RUN apt-get update -y && apt-get install -y openssl
RUN yarn global add prisma@6.7.0
ENV LIBRARY="/library"
ENV DATA="/data"
ENV NGINX_CONFIG="/nginx.conf"
# Nuxt's port
ENV PORT=4000
CMD ["sh", "/app/startup/launch.sh"]
CMD ["/app/startup/launch.sh"]
View File
+56 -16
View File
@@ -6,32 +6,72 @@
# Drop
[![Website](https://img.shields.io/badge/website-000000?style=for-the-badge&logo=About.me&logoColor=white)](https://droposs.org)
[![Docs](https://img.shields.io/badge/DOCS-black?style=for-the-badge&logo=docusaurus)](https://docs.droposs.org/)
[![Static Badge](https://img.shields.io/badge/FORUM-blue?style=for-the-badge)](https://forum.droposs.org)
[![GitHub License](https://img.shields.io/badge/AGPL--3.0-red?style=for-the-badge)](LICENSE)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/ACq4qZp4a9)
[![Open Collective](https://img.shields.io/badge/OpenCollective-1F87FF?style=for-the-badge&logo=OpenCollective&logoColor=white)](https://opencollective.com/drop-oss)
[![Weblate project translated](https://img.shields.io/weblate/progress/drop?server=https%3A%2F%2Ftranslate.droposs.org&style=for-the-badge&logo=weblate)
](https://translate.droposs.org/engage/drop/)
Drop is an open-source game distribution platform, similar to GameVault or Steam. It's designed to distribute and share DRM-free games quickly, all while being incredibly flexible, beautiful, and fast.
<div align="center">
<img src="https://droposs.org/_ipx/f_webp&q_80/images/carousel/store.png" alt="Drop Screenshot" width="900rem"/>
</div>
Drop is an open-source game distribution platform, like GameVault or Steam. It's designed to distribute and shared DRM-free game quickly, all while being incredibly flexible, beautiful and fast.
## Philosophy
1. Drop is flexible. While abstractions and interfaces can complicate the codebase, the flexibility is worth it.
2. Drop is secure. The nature of Drop means an instance can never be accessible without authentication. In line with #1, Drop also supports a huge variety of authentication mechanisms, from username/password to SSO.
3. Drop is user-friendly. The interface is designed to be clean and simple to use, with advanced features available to users who want them.
1. Drop is flexible. While abstractions and interfaces can make the codebase more complicated, the flexibility is worth it.
2. Drop is secure. The nature of Drop means an instance can never be accessible without authentication. In line with #1, Drop also supports a huge variety of authentication mechanisms, from a username/password to SSO.
3. Drop is user-friendly. The interface is designed to be clean and simple to use, with complexity available to the users who want it.
## Deployment
See our documentation on how to [deploy Drop](https://docs.droposs.org/docs/guides/quickstart) for more information.
To just deploy Drop, we've set up a simple docker compose file in deploy-template.
1. Generate a [GiantBomb API Key](https://www.giantbomb.com/api/)
2. Navigate to the deploy-template directory in your terminal (`cd deploy-template`)
3. Edit the compose.yml file (`nano compose.yml`) and copy your GiamtBomb API Key into the GIANT_BOMB_API_KEY environment variable
4. Run `docker compose up -d`
Your drop server should now be running. To register the admin user, navigate to http://your.drop.server.ip:3000/register?id=admin
and fill in the required forms
### Adding a game
To add a game to the drop library, do as follows:
1. Ensure that the current user owns the library folder with `sudo chown -R $(id -u $(whoami)) library`
2. `cd library`
3. `mkdir <GAME_NAME>` with the name of the game which you would like to register
4. `cd <GAME_NAME>`
5. `mkdir <VERSION_NAME>` Upload files for the specific game version to this folder
6. Navigate to http://your.drop.server.ip:3000/
7. Import game metadata (uses GiantBomb API Key) by selecting the game and specifying which entry to import
8. Navigate to http://your.drop.server.ip:3000/admin/library
9. You should see the game which you have just imported listed in this menu. There should be a notification that "Drop has detected you have new verions of this game to import". Select import here.
10. Select the game version to import and thus fill in fields as required.
## Tech Stack
This repo uses the Nuxt 3 + TailwindCSS stack, with the `yarn` package manager.
For the database, Drop uses Prisma connected to PostgreSQL.
## Development
To get started with development, you need `yarn --optional` and `docker compose` installed (or know how to set up a PostgreSQL database).
### Note: `--optional` flag is **REQUIRED**
Drop uses a utility package called droplet that's written in Rust. It has builts for Linux (GNU) and Windows, and they are set up as optional packages. `npm` installs these by default, but `yarn` needs the `--optional` flag.
Steps:
1. Run `git submodule update --init --recursive` to setup submodules
1. Copy the `.env.example` to `.env` and add your GiantBomb metadata key (more metadata providers coming)
1. Create the `.data` directory with `mkdir .data`
1. Ensure that your user owns the `.data` directory with `sudo chown -R $(id -u $(whoami))`
1. Open up a terminal and navigate to `dev-tools`, and run `docker compose up`
1. Open up another terminal in the root directory of the project and run `yarn` and then `yarn dev` to start the dev server
As part of the first-time bootstrap, Drop creates an invitation with the fixed id of 'admin'. So, to create an admin account, go to:
http://localhost:3000/auth/register?id=admin
## Contributing
Please see the [in-depth contributing guide](CONTRIBUTING.md). The guide includes information on how to set up the project, how to contribute code, how to report issues, and even how to effectively translate Drop.
[![Drop Translation Progress](https://translate.droposs.org/widget/drop/horizontal-auto.svg)](https://translate.droposs.org/engage/drop/)
Please see the [in-depth contributing guide](CONTRIBUTING.md)
+1 -1
View File
@@ -2,4 +2,4 @@
To report a vulnerability, please DO NOT create an issue for 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)
+23
View File
@@ -0,0 +1,23 @@
<template>
<NuxtLoadingIndicator color="#2563eb" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<ModalStack />
</template>
<script setup lang="ts">
await updateUser();
</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>
+4
View File
@@ -0,0 +1,4 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
@config "../tailwind.config.js";
-1
View File
@@ -1 +0,0 @@
/bin
-19
View File
@@ -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())
}
-10
View File
@@ -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
)
-15
View File
@@ -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=
-5
View File
@@ -1,5 +0,0 @@
module drop
go 1.26.1
require github.com/gorilla/mux v1.8.1
-2
View File
@@ -1,2 +0,0 @@
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-3
View File
@@ -1,3 +0,0 @@
go 1.26.1
use ./core
-9
View File
@@ -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=
-37
View File
@@ -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()
}
+3 -2
View File
@@ -2,7 +2,8 @@
# This file starts up the Drop server by running migrations and then starting the executable
echo "[Drop] performing migrations..."
pnpm prisma migrate deploy
ls ./prisma/migrations/
prisma migrate deploy
# Actually start the application
node /app/app/server/index.mjs
node /app/app/server/index.mjs
+352
View File
@@ -0,0 +1,352 @@
## 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
View File
@@ -1 +0,0 @@
use flake
-4
View File
@@ -1,4 +0,0 @@
/target
logs/
.vscode
.direnv
-3396
View File
File diff suppressed because it is too large Load Diff
-27
View File
@@ -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"
-3
View File
@@ -1,3 +0,0 @@
# CLI (`downpour`)
The cli way to access Drop. Used for admin tasks that require local access, like uploading game content.
-96
View File
@@ -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
}
-52
View File
@@ -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"
'';
};
}
);
}
-2
View File
@@ -1,2 +0,0 @@
[toolchain]
channel = "nightly"
-10
View File
@@ -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]
-69
View File
@@ -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,
}
-152
View File
@@ -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(())
}
-27
View File
@@ -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()))
}
}
-5
View File
@@ -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>;
}
-47
View File
@@ -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))
}
-7
View File
@@ -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;
-67
View File
@@ -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)
}
}
-41
View File
@@ -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),
}
}
}
-2
View File
@@ -1,2 +0,0 @@
pub mod connect;
pub mod upload;
-79
View File
@@ -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
View File
@@ -1 +0,0 @@
pub mod interface;
-53
View File
@@ -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(())
}
-34
View File
@@ -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(())
}
-114
View File
@@ -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
}
-5
View File
@@ -1,5 +0,0 @@
use opendal::Operator;
pub trait OperatorBuilder {
fn build(&self) -> anyhow::Result<Operator>;
}
@@ -1,7 +1,7 @@
<template>
<div class="flex grow flex-col gap-y-5 overflow-y-auto px-6 py-4">
<span class="inline-flex items-center gap-x-2 font-semibold text-zinc-100">
<UserIcon class="size-5" /> {{ $t("account.title") }}
<UserIcon class="size-5" /> Account Settings
</span>
<nav class="flex flex-1 flex-col">
<ul role="list" class="flex flex-1 flex-col gap-y-7">
@@ -45,55 +45,40 @@ import {
LockClosedIcon,
DevicePhoneMobileIcon,
WrenchScrewdriverIcon,
CodeBracketIcon,
} from "@heroicons/vue/24/outline";
import { UserIcon } from "@heroicons/vue/24/solid";
import type { Component } from "vue";
const notifications = useNotifications();
const { t } = useI18n();
const navigation: Ref<
(NavigationItem & { icon: Component; count?: number })[]
> = computed(() => [
const navigation: (NavigationItem & { icon: Component; count?: number })[] = [
{ label: "Home", route: "/account", icon: HomeIcon, prefix: "/account" },
{
label: t("account.home.title"),
route: "/account",
icon: HomeIcon,
prefix: "/account",
},
{
label: t("account.security.title"),
label: "Security",
route: "/account/security",
prefix: "/account/security",
icon: LockClosedIcon,
},
{
label: t("account.devices.title"),
label: "Devices",
route: "/account/devices",
prefix: "/account/devices",
icon: DevicePhoneMobileIcon,
},
{
label: t("account.token.title"),
route: "/account/tokens",
prefix: "/account/tokens",
icon: CodeBracketIcon,
},
{
label: t("account.notifications.notifications"),
label: "Notifications",
route: "/account/notifications",
prefix: "/account/notifications",
icon: BellIcon,
count: notifications.value.length,
},
{
label: t("account.settings"),
label: "Settings",
route: "/account/settings",
prefix: "/account/settings",
icon: WrenchScrewdriverIcon,
},
]);
];
const currentPageIndex = useCurrentNavigationIndex(navigation.value);
const currentPageIndex = useCurrentNavigationIndex(navigation);
</script>
@@ -8,7 +8,7 @@
class="transition w-full inline-flex items-center justify-center h-full gap-x-2 rounded-none rounded-l-md bg-white/10 hover:bg-white/20 text-zinc-100 backdrop-blur px-5 py-3 active:scale-95"
@click="() => toggleLibrary()"
>
{{ inLibrary ? $t("library.inLib") : $t("library.addToLib") }}
{{ inLibrary ? "In Library" : "Add to Library" }}
<CheckIcon v-if="inLibrary" class="-mr-0.5 h-5 w-5" aria-hidden="true" />
<PlusIcon v-else class="-mr-0.5 h-5 w-5" aria-hidden="true" />
</LoadingButton>
@@ -36,7 +36,7 @@
<div
class="font-display uppercase px-3 py-2 text-sm font-semibold text-zinc-500"
>
{{ $t("library.collection.collections") }}
Collections
</div>
<div
class="flex flex-col gap-y-2 py-1 max-h-[150px] overflow-y-auto"
@@ -45,7 +45,7 @@
v-if="collections.length === 0"
class="px-3 py-2 text-sm text-zinc-500"
>
{{ $t("library.collection.noCollections") }}
No collections
</div>
<MenuItem
v-for="(collection, collectionIdx) in collections"
@@ -75,7 +75,7 @@
@click="createCollectionModal = true"
>
<PlusIcon class="mr-2 h-4 w-4" />
{{ $t("library.collection.addToNew") }}
Add to new collection
</LoadingButton>
</div>
</div>
@@ -84,7 +84,7 @@
</Menu>
</div>
<ModalCreateCollection
<CreateCollectionModal
v-model="createCollectionModal"
:game-id="props.gameId"
/>
@@ -100,7 +100,6 @@ const props = defineProps<{
const isLibraryLoading = ref(false);
const { t } = useI18n();
const createCollectionModal = ref(false);
const collections = await useCollections();
const library = await useLibrary();
@@ -122,9 +121,18 @@ async function toggleLibrary() {
body: {
id: props.gameId,
},
failTitle: t("errors.library.add.title"),
});
await refreshLibrary();
} catch (e) {
createModal(
ModalType.Notification,
{
title: "Failed to add game to library",
// @ts-expect-error attempt to display statusMessage on error
description: `Drop couldn't add this game to your library: ${e?.statusMessage}`,
},
(_, c) => c(),
);
} finally {
isLibraryLoading.value = false;
}
@@ -136,18 +144,24 @@ async function toggleCollection(id: string) {
if (!collection) return;
const index = collection.entries.findIndex((e) => e.gameId == props.gameId);
await $dropFetch(`/api/v1/collection/:id/entry`, {
await $dropFetch(`/api/v1/collection/${id}/entry`, {
method: index == -1 ? "POST" : "DELETE",
params: { id },
body: {
id: props.gameId,
},
failTitle: t("errors.library.add.title"),
});
await refreshCollection(id);
} finally {
/* empty */
} catch (e) {
createModal(
ModalType.Notification,
{
title: "Failed to add game to library",
// @ts-expect-error attempt to display statusMessage on error
description: `Drop couldn't add this game to your library: ${e?.statusMessage}`,
},
(_, c) => c(),
);
}
}
</script>
+10
View File
@@ -0,0 +1,10 @@
<template>
<div class="flex">
<a
href="/auth/oidc"
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"
>
Sign in with external provider &rarr;
</a>
</div>
</template>
@@ -4,7 +4,7 @@
<label
for="username"
class="block text-sm font-medium leading-6 text-zinc-300"
>{{ $t("auth.username") }}</label
>Username</label
>
<div class="mt-2">
<input
@@ -12,7 +12,7 @@
v-model="username"
name="username"
type="username"
autocomplete="username webauthn"
autocomplete="username"
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"
/>
@@ -23,7 +23,7 @@
<label
for="password"
class="block text-sm font-medium leading-6 text-zinc-300"
>{{ $t("auth.password") }}</label
>Password</label
>
<div class="mt-2">
<input
@@ -50,23 +50,19 @@
<label
for="remember-me"
class="ml-3 block text-sm leading-6 text-zinc-400"
>{{ $t("auth.signin.rememberMe") }}</label
>Remember me</label
>
</div>
<div class="text-sm leading-6">
<NuxtLink
to="#"
class="font-semibold text-blue-600 hover:text-blue-500"
>{{ $t("auth.signin.forgot") }}</NuxtLink
<NuxtLink to="#" class="font-semibold text-blue-600 hover:text-blue-500"
>Forgot password?</NuxtLink
>
</div>
</div>
<div>
<LoadingButton class="w-full" :loading="loading">{{
$t("auth.signin.signin")
}}</LoadingButton>
<LoadingButton class="w-full" :loading="loading"> Sign in</LoadingButton>
</div>
<div v-if="error" class="mt-1 rounded-md bg-red-600/10 p-4">
@@ -86,78 +82,35 @@
<script setup lang="ts">
import { XCircleIcon } from "@heroicons/vue/20/solid";
import {
startAuthentication,
browserSupportsWebAuthn,
} from "@simplewebauthn/browser";
import { FetchError } from "ofetch";
import type { User } from "~/prisma/client";
const username = ref("");
const password = ref("");
const rememberMe = 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 router = useRouter();
const route = useRoute();
const { t } = useI18n();
const router = useRouter();
async function signin_wrapper() {
function signin_wrapper() {
loading.value = true;
try {
await signin();
} catch (e) {
if (e instanceof FetchError) {
error.value = e.data.message || t("errors.unknown");
} else {
error.value = e as string;
}
} finally {
loading.value = false;
}
signin()
.then(() => {
router.push(route.query.redirect?.toString() ?? "/");
})
.catch((response) => {
const message = response.statusMessage || "An unknown error occurred";
error.value = message;
})
.finally(() => {
loading.value = false;
});
}
async function signin() {
const { result } = await $dropFetch("/api/v1/auth/signin/simple", {
await $dropFetch("/api/v1/auth/signin/simple", {
method: "POST",
body: {
username: username.value,
@@ -165,11 +118,7 @@ async function signin() {
rememberMe: rememberMe.value,
},
});
if (result == "2fa") {
router.push({ query: route.query, path: "/auth/mfa" });
return;
}
await completeSignin();
const user = useUser();
user.value = await $dropFetch<User | null>("/api/v1/user");
}
</script>
@@ -4,10 +4,9 @@
v-for="(_, i) in amount"
:key="i"
:class="[
carousel.currentSlide === i ? 'bg-blue-600 w-6' : 'bg-zinc-700 w-3',
carousel.currentSlide == i ? 'bg-blue-600 w-6' : 'bg-zinc-700 w-3',
'transition-all cursor-pointer h-2 rounded-full',
]"
@click="slideTo(i)"
/>
</div>
</template>
@@ -19,8 +18,8 @@ const carousel = inject(injectCarousel)!;
const amount = carousel.maxSlide - carousel.minSlide + 1;
function slideTo(index: number) {
const offsetIndex = index + carousel.minSlide;
carousel.nav.slideTo(offsetIndex);
}
// function slideTo(index: number) {
// const offsetIndex = index + carousel.minSlide;
// carousel.nav.slideTo(offsetIndex);
// }
</script>
@@ -3,10 +3,11 @@
<template #default>
<div>
<DialogTitle as="h3" class="text-lg font-medium leading-6 text-white">
{{ $t("library.collection.create") }}
Create collection
</DialogTitle>
<p class="mt-1 text-zinc-400 text-sm">
{{ $t("library.collection.createDesc") }}
Collections can used to organise your games and find them more easily,
especially if you have a large library.
</p>
</div>
<div class="mt-2">
@@ -14,7 +15,7 @@
<input
v-model="collectionName"
type="text"
:placeholder="$t('library.collection.namePlaceholder')"
placeholder="Collection name"
class="block w-full rounded-md border-0 bg-zinc-800 py-1.5 text-white shadow-sm ring-1 ring-inset ring-zinc-700 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
/>
<button class="hidden" type="submit" />
@@ -29,7 +30,7 @@
class="w-full sm:w-fit"
@click="() => createCollection()"
>
{{ $t("common.create") }}
Create
</LoadingButton>
<button
ref="cancelButtonRef"
@@ -37,7 +38,7 @@
class="mt-3 inline-flex w-full justify-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-800 hover:bg-zinc-900 sm:mt-0 sm:w-auto"
@click="() => close()"
>
{{ $t("cancel") }}
Cancel
</button>
</template>
</ModalTemplate>
@@ -46,7 +47,7 @@
<script setup lang="ts">
import { ref } from "vue";
import { DialogTitle } from "@headlessui/vue";
import type { CollectionEntryModel, GameModel } from "~/prisma/client/models";
import type { CollectionEntry, Game } from "~/prisma/client";
import type { SerializeObject } from "nitropack";
const props = defineProps<{
@@ -73,17 +74,15 @@ async function createCollection() {
const response = await $dropFetch("/api/v1/collection", {
method: "POST",
body: { name: collectionName.value },
failTitle: "Failed to create collection",
});
// Add the game if provided
if (props.gameId) {
const entry = await $dropFetch<
CollectionEntryModel & { game: SerializeObject<GameModel> }
CollectionEntry & { game: SerializeObject<Game> }
>(`/api/v1/collection/${response.id}/entry`, {
method: "POST",
body: { id: props.gameId },
failTitle: "Failed to add game to collection",
});
response.entries.push(entry);
}
@@ -95,6 +94,18 @@ async function createCollection() {
open.value = false;
emit("created", response.id);
} catch (error) {
console.error("Failed to create collection:", error);
const err = error as { statusMessage?: string };
createModal(
ModalType.Notification,
{
title: "Failed to create collection",
description: `Drop couldn't create your collection: ${err?.statusMessage}`,
},
(_, c) => c(),
);
} finally {
createCollectionLoading.value = false;
}
@@ -6,13 +6,13 @@
as="h3"
class="text-lg font-bold font-display text-zinc-100"
>
{{ $t("library.collection.delete") }}
Delete Collection
</DialogTitle>
<p class="mt-1 text-sm text-zinc-400">
{{ $t("common.deleteConfirm", [collection?.name]) }}
Are you sure you want to delete "{{ collection?.name }}"?
</p>
<p class="mt-2 text-sm font-bold text-red-500">
{{ $t("common.cannotUndo") }}
This action cannot be undone.
</p>
</div>
</template>
@@ -22,38 +22,35 @@
class="bg-red-600 text-white hover:bg-red-500"
@click="() => deleteCollection()"
>
{{ $t("common.delete") }}
Delete
</LoadingButton>
<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"
@click="() => (collection = undefined)"
>
{{ $t("cancel") }}
Cancel
</button>
</template>
</ModalTemplate>
</template>
<script setup lang="ts">
import type { CollectionModel } from "~/prisma/client/models";
import type { Collection } from "~/prisma/client";
import { DialogTitle } from "@headlessui/vue";
const collection = defineModel<CollectionModel | undefined>();
const collection = defineModel<Collection | undefined>();
const deleteLoading = ref(false);
const collections = await useCollections();
const { t } = useI18n();
async function deleteCollection() {
try {
if (!collection.value) return;
deleteLoading.value = true;
await $dropFetch(`/api/v1/collection/:id`, {
await $dropFetch(`/api/v1/collection/${collection.value.id}`, {
// @ts-expect-error not documented
method: "DELETE",
params: {
id: collection.value.id,
},
});
const index = collections.value.findIndex(
(e) => e.id == collection.value?.id,
@@ -65,11 +62,9 @@ async function deleteCollection() {
createModal(
ModalType.Notification,
{
title: t("errors.library.add.title"),
description: t("errors.library.add.desc", [
// @ts-expect-error attempt to display statusMessage on error
e?.statusMessage ?? t("errors.unknown"),
]),
title: "Failed to add game to library",
// @ts-expect-error attempt to display statusMessage on error
description: `Drop couldn't add this game to your library: ${e?.statusMessage}`,
},
(_, c) => c(),
);
@@ -6,13 +6,13 @@
as="h3"
class="text-lg font-bold font-display text-zinc-100"
>
{{ $t("news.delete") }}
Delete Article
</DialogTitle>
<p class="mt-1 text-sm text-zinc-400">
{{ $t("common.deleteConfirm", [article?.title]) }}
Are you sure you want to delete "{{ article?.title }}"?
</p>
<p class="mt-2 text-sm font-bold text-red-500">
{{ $t("common.cannotUndo") }}
This action cannot be undone.
</p>
</div>
</template>
@@ -22,13 +22,13 @@
class="bg-red-600 text-white hover:bg-red-500"
@click="() => deleteArticle()"
>
{{ $t("common.delete") }}
Delete
</LoadingButton>
<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"
@click="() => (article = undefined)"
>
{{ $t("cancel") }}
Cancel
</button>
</template>
</ModalTemplate>
@@ -45,7 +45,6 @@ interface Article {
const article = defineModel<Article | undefined>();
const deleteLoading = ref(false);
const router = useRouter();
const { t } = useI18n();
const news = useNews();
if (!news.value) {
news.value = await fetchNews();
@@ -69,11 +68,9 @@ async function deleteArticle() {
createModal(
ModalType.Notification,
{
title: t("errors.news.article.delete.title"),
description: t("errors.news.article.delete.desc", [
// @ts-expect-error attempt to display statusMessage on error
e?.statusMessage ?? t("errors.unknown"),
]),
title: "Failed to delete article",
// @ts-expect-error attempt to display statusMessage on error
description: `Drop couldn't delete this article: ${e?.statusMessage}`,
},
(_, c) => c(),
);
@@ -1,17 +1,14 @@
<svg
<template>
<svg
class="text-blue-400"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet"
>
<style>
:root {
color: oklch(70.7% 0.165 254.624)
}
</style>
<path
d="M4 13.5C4 11.0008 5.38798 8.76189 7.00766 7C8.43926 5.44272 10.0519 4.25811 11.0471 3.5959C11.6287 3.20893 12.3713 3.20893 12.9529 3.5959C13.9481 4.25811 15.5607 5.44272 16.9923 7C18.612 8.76189 20 11.0008 20 13.5C20 17.9183 16.4183 21.5 12 21.5C7.58172 21.5 4 17.9183 4 13.5Z"
stroke="currentColor"
stroke-width="2"
/>
</svg>
</svg>
</template>

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 503 B

@@ -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"
/>
</svg>
<ApplicationLogo aria-hidden="true" class="h-6" />
<span class="text-blue-400 font-display font-bold text-xl uppercase">
<template v-if="serverName">
{{ serverName }}
</template>
<template v-else>
{{ $t("drop.drop") }}
</template>
</span>
<DropLogo class="h-6" />
<span class="text-blue-400 font-display font-bold text-xl uppercase"
>Drop</span
>
</div>
</template>
<script setup lang="ts">
const { serverName } = await $dropFetch("/api/v1");
</script>
@@ -7,11 +7,7 @@
:key="gameIdx"
class="justify-start"
>
<GamePanel
:game="game"
:href="game ? `/store/${game.id}` : undefined"
:show-title-description="showGamePanelTextDecoration"
/>
<GamePanel :game="game" />
</VueSlide>
<template #addons>
@@ -35,23 +31,19 @@
</template>
<script setup lang="ts">
import type { GameModel } from "~/prisma/client/models";
import type { Game } from "~/prisma/client";
import type { SerializeObject } from "nitropack";
const props = defineProps<{
items: Array<SerializeObject<GameModel>>;
items: Array<SerializeObject<Game>>;
min?: number;
width?: number;
}>();
const {
store: { showGamePanelTextDecoration },
} = await $dropFetch(`/api/v1/settings`);
const currentComponent = ref<HTMLDivElement>();
const min = computed(() => Math.max(props.min ?? 8, props.items.length));
const games: Ref<Array<SerializeObject<GameModel> | undefined>> = computed(() =>
const games: Ref<Array<SerializeObject<Game> | undefined>> = computed(() =>
Array(min.value)
.fill(0)
.map((_, i) => props.items[i]),
+61
View File
@@ -0,0 +1,61 @@
<template>
<NuxtLink
v-if="game"
:href="props.href ?? `/store/${game.id}`"
class="group relative w-48 h-64 rounded-lg overflow-hidden transition-all duration-300 text-left hover:scale-[1.02] hover:shadow-lg hover:-translate-y-0.5"
@click="active = game.id"
>
<div
class="absolute inset-0 transition-all duration-300 group-hover:scale-110"
>
<img
:src="useObject(game.mCoverObjectId)"
class="w-full h-full object-cover brightness-[90%]"
:class="{ active: active === game.id }"
:alt="game.mName"
/>
<div
class="absolute inset-0 bg-gradient-to-t from-zinc-950/80 via-zinc-950/20 to-transparent"
/>
</div>
<div class="absolute bottom-0 left-0 w-full p-3">
<h1
class="text-zinc-100 text-sm font-bold font-display group-hover:text-white transition-colors"
>
{{ game.mName }}
</h1>
<p
class="text-zinc-400 text-xs line-clamp-2 group-hover:text-zinc-300 transition-colors"
>
{{ game.mShortDescription }}
</p>
</div>
</NuxtLink>
<SkeletonCard v-else message="no game" />>
</template>
<script setup lang="ts">
import type { SerializeObject } from "nitropack";
const props = defineProps<{
game:
| SerializeObject<{
id: string;
mCoverObjectId: string;
mName: string;
mShortDescription: string;
}>
| undefined;
href?: string;
}>();
const active = useState();
</script>
<style scoped>
img.active {
view-transition-name: selected-game;
contain: layout;
}
</style>
@@ -1,9 +1,6 @@
<template>
<div class="flex flex-row items-center gap-x-2">
<img
:src="rawIcon ? game.icon : useObject(game.icon)"
class="w-12 h-12 rounded-sm object-cover"
/>
<img :src="game.icon" class="w-12 h-12 rounded-sm object-cover" />
<div class="flex flex-col items-left">
<h1 class="font-semibold font-display text-lg text-zinc-100">
{{ game.name }}
@@ -21,8 +18,7 @@
<script setup lang="ts">
import type { GameMetadataSearchResult } from "~/server/internal/metadata/types";
const { game, rawIcon = true } = defineProps<{
game: Omit<GameMetadataSearchResult, "year"> & { sourceName?: string };
rawIcon?: boolean;
const { game } = defineProps<{
game: GameMetadataSearchResult & { sourceName?: string };
}>();
</script>
@@ -1,7 +1,7 @@
<template>
<div class="flex grow flex-col overflow-y-auto px-6 py-4">
<span class="inline-flex items-center gap-x-2 font-semibold text-zinc-100">
<Bars3Icon class="size-6" /> {{ $t("userHeader.links.library") }}
<Bars3Icon class="size-6" /> Library
</span>
<!-- Search bar -->
@@ -13,7 +13,7 @@
name="search"
autocomplete="off"
class="block w-full rounded-md bg-zinc-900 py-2 pl-9 pr-2 text-sm text-zinc-100 outline outline-1 -outline-offset-1 outline-zinc-700 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-blue-600"
:placeholder="$t('library.search')"
placeholder="Search library..."
/>
<MagnifyingGlassIcon
class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-zinc-400"
@@ -31,11 +31,11 @@
<li v-for="game in filteredLibrary" :key="game.id" class="flex">
<NuxtLink
:to="`/library/game/${game.id}`"
class="flex flex-row items-center w-full p-2 rounded-md transition-all duration-200 hover:bg-zinc-800 hover:scale-105 hover:shadow-lg active:scale-95"
class="flex flex-row items-center w-full p-1 rounded-md transition-all duration-200 hover:bg-zinc-800 hover:scale-105 hover:shadow-lg active:scale-95"
>
<img
:src="useObject(game.mIconObjectId)"
class="h-5 flex-shrink-0 rounded transition-all duration-300 group-hover:scale-105 hover:rotate-[-2deg] hover:shadow-lg"
:src="useObject(game.mCoverObjectId)"
class="h-9 w-9 flex-shrink-0 rounded transition-all duration-300 group-hover:scale-105 hover:rotate-[-2deg] hover:shadow-lg"
alt=""
/>
<div class="min-w-0 flex-1 pl-2.5">
@@ -53,7 +53,7 @@
v-else
class="text-zinc-600 text-sm font-display font-bold uppercase text-center mt-8"
>
{{ !!searchQuery ? $t("common.noResults") : $t("library.noGames") }}
{{ !!searchQuery ? "No results" : "No games in library" }}
</p>
</div>
</template>
@@ -11,18 +11,18 @@
class="h-5 w-5 transition-transform duration-200"
:class="{ 'rotate-90': modalOpen }"
/>
<span>{{ $t("news.article.new") }}</span>
<span>New article</span>
</button>
<ModalTemplate v-model="modalOpen" size-class="sm:max-w-[80vw]">
<h3 class="text-lg font-semibold text-zinc-100 mb-4">
{{ $t("news.article.create") }}
Create New Article
</h3>
<form class="space-y-4" @submit.prevent="() => createArticle()">
<div>
<label for="title" class="block text-sm font-medium text-zinc-400">{{
$t("news.article.titles")
}}</label>
<label for="title" class="block text-sm font-medium text-zinc-400"
>Title</label
>
<input
id="title"
v-model="newArticle.title"
@@ -34,10 +34,8 @@
</div>
<div>
<label
for="excerpt"
class="block text-sm font-medium text-zinc-400"
>{{ $t("news.article.shortDesc") }}</label
<label for="excerpt" class="block text-sm font-medium text-zinc-400"
>Short description</label
>
<input
id="excerpt"
@@ -49,10 +47,8 @@
</div>
<div>
<label
for="content"
class="block text-sm font-medium text-zinc-400"
>{{ $t("news.article.content") }}</label
<label for="content" class="block text-sm font-medium text-zinc-400"
>Content (Markdown)</label
>
<div class="mt-1 flex flex-col gap-4">
<!-- Markdown shortcuts -->
@@ -73,9 +69,7 @@
>
<!-- Editor -->
<div class="flex flex-col">
<span class="text-sm text-zinc-500 mb-2">{{
$t("news.article.editor")
}}</span>
<span class="text-sm text-zinc-500 mb-2">Editor</span>
<textarea
id="content"
ref="contentEditor"
@@ -88,9 +82,7 @@
<!-- Preview -->
<div class="flex flex-col">
<span class="text-sm text-zinc-500 mb-2">{{
$t("news.article.preview")
}}</span>
<span class="text-sm text-zinc-500 mb-2">Preview</span>
<div
class="flex-1 p-4 rounded-md bg-zinc-900 border border-zinc-700 overflow-y-auto"
>
@@ -103,7 +95,8 @@
</div>
</div>
<p class="mt-2 text-sm text-zinc-500">
{{ $t("news.article.editorGuide") }}
Use the shortcuts above or write Markdown directly. Supports
**bold**, *italic*, [links](url), and more.
</p>
</div>
@@ -121,7 +114,7 @@
/>
<span
class="transition mt-2 block text-sm font-semibold text-zinc-400 group-hover:text-zinc-500"
>{{ $t("news.article.uploadCover") }}</span
>Upload cover image</span
>
<p v-if="currentFile" class="mt-1 text-xs text-zinc-400">
{{ currentFile.name }}
@@ -132,14 +125,14 @@
accept="image/*"
class="hidden"
type="file"
@change="(e: Event) => (file = (e.target as any)?.files)"
@change="(e) => (file = (e.target as any)?.files)"
/>
</div>
<div>
<label class="block text-sm font-medium text-zinc-400 mb-2">{{
$t("common.tags")
}}</label>
<label class="block text-sm font-medium text-zinc-400 mb-2"
>Tags</label
>
<div class="flex flex-wrap gap-2 mb-2">
<span
v-for="tag in newArticle.tags"
@@ -160,7 +153,7 @@
<input
v-model="newTagInput"
type="text"
:placeholder="$t('news.article.tagPlaceholder')"
placeholder="Add a tag..."
class="mt-1 block w-full rounded-md bg-zinc-900 border-zinc-700 text-zinc-100 shadow-sm focus:border-primary-500 focus:ring-primary-500"
@keydown.enter.prevent="addTag"
/>
@@ -169,7 +162,7 @@
class="mt-1 px-3 py-2 rounded-md bg-zinc-800 text-zinc-100 hover:bg-zinc-700"
@click="addTag"
>
{{ $t("news.article.add") }}
Add
</button>
</div>
</div>
@@ -193,16 +186,15 @@
<LoadingButton
:loading="loading"
class="bg-blue-600 text-white hover:bg-blue-500"
:disabled="!isValidArticle"
@click="() => createArticle()"
>
{{ $t("news.article.submit") }}
Submit
</LoadingButton>
<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"
@click="() => (modalOpen = !modalOpen)"
>
{{ $t("cancel") }}
Cancel
</button>
</template>
</ModalTemplate>
@@ -236,13 +228,6 @@ const newArticle = ref({
tags: [] as string[],
});
const isValidArticle = computed(
() =>
newArticle.value.title &&
newArticle.value.description &&
newArticle.value.content,
);
const markdownPreview = computed(() => {
// TODO: maybe?? add https://github.com/cure53/DOMPurify
// micromark says its safe, but this is straight html we are injecting
@@ -251,49 +236,18 @@ const markdownPreview = computed(() => {
const file = ref<FileList | undefined>();
const currentFile = computed(() => file.value?.item(0));
const { t } = useI18n();
const error = ref<string | undefined>();
const contentEditor = ref<HTMLTextAreaElement>();
const markdownShortcuts = [
{
label: t("editor.bold"),
prefix: "**",
suffix: "**",
placeholder: t("editor.boldPlaceholder"),
},
{
label: t("editor.italic"),
prefix: "_",
suffix: "_",
placeholder: t("editor.italicPlaceholder"),
},
{
label: t("editor.link"),
prefix: "[",
suffix: "](url)",
placeholder: t("editor.linkPlaceholder"),
},
{
label: t("editor.code"),
prefix: "`",
suffix: "`",
placeholder: t("editor.codePlaceholder"),
},
{
label: t("editor.listItem"),
prefix: "- ",
suffix: "",
placeholder: t("editor.listItemPlaceholder"),
},
{
label: t("editor.heading"),
prefix: "## ",
suffix: "",
placeholder: t("editor.headingPlaceholder"),
},
{ label: "Bold", prefix: "**", suffix: "**", placeholder: "bold text" },
{ label: "Italic", prefix: "_", suffix: "_", placeholder: "italic text" },
{ label: "Link", prefix: "[", suffix: "](url)", placeholder: "link text" },
{ label: "Code", prefix: "`", suffix: "`", placeholder: "code" },
{ label: "List Item", prefix: "- ", suffix: "", placeholder: "list item" },
{ label: "Heading", prefix: "## ", suffix: "", placeholder: "heading" },
];
function handleContentKeydown(e: KeyboardEvent) {
@@ -415,7 +369,7 @@ async function createArticle() {
modalOpen.value = false;
} catch (e) {
// @ts-expect-error attempt to get statusMessage on error
error.value = e?.statusMessage ?? t("errors.unknown");
error.value = e?.statusMessage ?? "An unknown error occured.";
} finally {
loading.value = false;
}
@@ -6,7 +6,7 @@
<!-- Search and filters -->
<div class="space-y-6">
<div>
<label for="search" class="sr-only">{{ $t("news.search") }}</label>
<label for="search" class="sr-only">Search articles</label>
<div class="relative">
<div
class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"
@@ -21,35 +21,31 @@
v-model="searchQuery"
type="text"
class="block w-full rounded-md border-0 bg-zinc-800 py-2.5 pl-10 pr-3 text-zinc-100 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6"
:placeholder="$t('news.searchPlaceholder')"
placeholder="Search articles..."
/>
</div>
</div>
<div class="pt-2">
<label
for="date"
class="block text-sm font-medium text-zinc-400 mb-2"
>{{ $t("common.date") }}</label
<label for="date" class="block text-sm font-medium text-zinc-400 mb-2"
>Date</label
>
<select
id="date"
v-model="dateFilter"
class="mt-1 block w-full rounded-md border-0 bg-zinc-800 py-2 pl-3 pr-10 text-zinc-100 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6"
>
<option value="all">{{ $t("news.filter.all") }}</option>
<option value="today">{{ $t("common.today") }}</option>
<option value="week">{{ $t("news.filter.week") }}</option>
<option value="month">{{ $t("news.filter.month") }}</option>
<option value="year">{{ $t("news.filter.year") }}</option>
<option value="all">All time</option>
<option value="today">Today</option>
<option value="week">This week</option>
<option value="month">This month</option>
<option value="year">This year</option>
</select>
</div>
<!-- Tags -->
<div>
<label class="block text-sm font-medium text-zinc-400 mb-2">
{{ $t("common.tags") }}
</label>
<label class="block text-sm font-medium text-zinc-400 mb-2">Tags</label>
<div class="flex flex-wrap gap-2">
<button
v-for="tag in availableTags"
@@ -91,7 +87,9 @@
:src="useObject(article.imageObjectId)"
class="absolute blur-sm inset-0 w-full h-full object-cover transition-all duration-200 group-hover:scale-110"
/>
<div class="absolute inset-0 bg-zinc-900/50" />
<div
class="absolute inset-0 bg-gradient-to-b from-transparent to-zinc-800 transition-all duration-200"
/>
</div>
<h3 class="relative text-sm font-medium text-zinc-100">
@@ -104,9 +102,9 @@
<div
class="relative mt-2 flex items-center gap-x-2 text-xs text-zinc-500"
>
<time :datetime="article.publishedAt">
{{ $d(new Date(article.publishedAt), "short") }}
</time>
<time :datetime="article.publishedAt">{{
formatDate(article.publishedAt)
}}</time>
</div>
</div>
</NuxtLink>
@@ -148,9 +146,20 @@ const toggleTag = (tag: string) => {
}
};
const formatDate = (date: string) => {
return new Date(date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
};
const formatExcerpt = (excerpt: string) => {
// Convert markdown to HTML, micromark is safe
return micromark(excerpt);
// TODO: same as one in NewsArticleCreateButton
// Convert markdown to HTML
const html = micromark(excerpt);
// Strip HTML tags using regex
return html.replace(/<[^>]*>/g, "");
};
const filteredArticles = computed(() => {
@@ -24,6 +24,7 @@
>
{{ name }}
</NuxtLink>
<!-- todo -->
</div>
</div>
<div class="ml-4 flex shrink-0">
@@ -32,7 +33,7 @@
class="inline-flex rounded-md text-zinc-400 hover:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
@click="() => deleteMe()"
>
<span class="sr-only">{{ $t("common.close") }}</span>
<span class="sr-only">Close</span>
<XMarkIcon class="size-5" aria-hidden="true" />
</button>
</div>
@@ -43,19 +44,13 @@
<script setup lang="ts">
import { XMarkIcon } from "@heroicons/vue/24/solid";
import type { SerializeObject } from "nitropack";
import type { NotificationModel } from "~/prisma/client/models";
import type { Notification } from "~/prisma/client";
const props = defineProps<{
notification: SerializeObject<NotificationModel>;
}>();
const props = defineProps<{ notification: Notification }>();
async function deleteMe() {
await $dropFetch(`/api/v1/notifications/:id`, {
await $dropFetch(`/api/v1/notifications/${props.notification.id}`, {
method: "DELETE",
params: {
id: props.notification.id,
},
});
const notifications = useNotifications();
const indexOfMe = notifications.value.findIndex(
@@ -15,7 +15,7 @@
/>
<span class="ml-3 block truncate">{{ model }}</span>
</span>
<span v-else>{{ $t("library.admin.import.selectPlatform") }}</span>
<span v-else>Please select a platform...</span>
<span
class="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2"
>
@@ -32,7 +32,7 @@
class="absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-zinc-950 ring-opacity-5 focus:outline-none sm:text-sm"
>
<ListboxOption
v-for="[name, value] in values"
v-for="[name, value] in Object.entries(values)"
:key="value"
v-slot="{ active, selected }"
as="template"
@@ -82,11 +82,10 @@ import {
ListboxOptions,
} from "@headlessui/vue";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import { Platform } from "~/prisma/client/enums";
const model = defineModel<Platform | undefined>();
const model = defineModel<PlatformClient | undefined>();
const typedModel = computed<Platform | null>({
const typedModel = computed<PlatformClient | null>({
get() {
return model.value || null;
},
@@ -96,5 +95,5 @@ const typedModel = computed<Platform | null>({
},
});
const values = Object.entries(Platform);
const values = Object.fromEntries(Object.entries(PlatformClient));
</script>
@@ -49,38 +49,31 @@
/>
<span
class="transition mt-2 block text-sm font-semibold text-zinc-400 group-hover:text-zinc-500"
>{{ $t("uploadFile") }}</span
>Upload file</span
>
<div v-if="currentFileList">
<p
v-for="currentFile in currentFileList"
:key="currentFile"
class="mt-1 text-[10px] text-zinc-500 whitespace-nowrap"
>
{{ currentFile }}
</p>
</div>
<p v-if="currentFile" class="mt-1 text-xs text-zinc-400">
{{ currentFile.name }}
</p>
</label>
<input
id="file-upload"
:accept="props.accept"
class="hidden"
type="file"
:multiple="props.multiple"
@change="(e: Event) => (file = (e.target as any)?.files)"
@change="(e) => (file = (e.target as any)?.files)"
/>
</div>
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<LoadingButton
:disabled="currentFiles == undefined"
:disabled="currentFile == undefined"
type="button"
:loading="uploadLoading"
:class="['inline-flex w-full shadow-sm sm:ml-3 sm:w-auto']"
@click="() => uploadFile_wrapper()"
>
{{ $t("upload") }}
Upload
</LoadingButton>
<button
ref="cancelButtonRef"
@@ -88,7 +81,7 @@
class="mt-3 inline-flex w-full justify-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-800 hover:bg-zinc-900 sm:mt-0 sm:w-auto"
@click="open = false"
>
{{ $t("cancel") }}
Cancel
</button>
</div>
<div v-if="uploadError" class="mt-3 rounded-md bg-red-600/10 p-4">
@@ -129,21 +122,11 @@ const open = defineModel<boolean>({
required: true,
});
const { t } = useI18n();
const file = ref<FileList | undefined>();
const currentFiles = computed(() => file.value);
const currentFileList = computed(() => {
if (!currentFiles.value) return undefined;
const list = [];
for (const file of currentFiles.value) {
list.push(file.name);
}
return list;
});
const currentFile = computed(() => file.value?.item(0));
const props = defineProps<{
endpoint: string;
accept: string;
multiple?: boolean;
options?: { [key: string]: string };
}>();
const emit = defineEmits(["upload"]);
@@ -151,12 +134,10 @@ const emit = defineEmits(["upload"]);
const uploadLoading = ref(false);
const uploadError = ref<string | undefined>();
async function uploadFile() {
if (!currentFiles.value) return;
if (!currentFile.value) return;
const form = new FormData();
for (const file of currentFiles.value) {
form.append(file.name, file);
}
form.append("file", currentFile.value);
if (props.options) {
for (const [key, value] of Object.entries(props.options)) {
@@ -177,7 +158,7 @@ function uploadFile_wrapper() {
uploadLoading.value = true;
uploadFile()
.catch((error) => {
uploadError.value = error.statusMessage ?? t("errors.unknown");
uploadError.value = error.statusMessage ?? "An unknown error occurred.";
})
.finally(() => {
uploadLoading.value = false;
@@ -1,17 +1,14 @@
<template>
<footer class="bg-zinc-950" aria-labelledby="footer-heading">
<h2 id="footer-heading" class="sr-only">{{ $t("footer.footer") }}</h2>
<h2 id="footer-heading" class="sr-only">Footer</h2>
<div class="mx-auto max-w-7xl px-6 py-16 sm:py-24 lg:px-8">
<!-- Drop Info -->
<div class="xl:grid xl:grid-cols-3 xl:gap-8">
<div class="space-y-8">
<DropWordmark class="h-10" />
<p class="text-sm leading-6 text-zinc-300">
{{ $t("drop.desc") }}
An open-source game distribution platform built for speed,
flexibility and beauty.
</p>
<SelectorLanguage />
<div class="flex space-x-6">
<NuxtLink
v-for="item in navigation.social"
@@ -25,14 +22,10 @@
</NuxtLink>
</div>
</div>
<!-- Foot links -->
<div class="mt-16 grid grid-cols-2 gap-8 xl:col-span-2 xl:mt-0">
<div class="md:grid md:grid-cols-2 md:gap-8">
<div>
<h3 class="text-sm font-semibold leading-6 text-white">
{{ $t("footer.games") }}
</h3>
<h3 class="text-sm font-semibold leading-6 text-white">Games</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in navigation.games" :key="item.name">
<NuxtLink
@@ -45,7 +38,7 @@
</div>
<div class="mt-10 md:mt-0">
<h3 class="text-sm font-semibold leading-6 text-white">
{{ $t("userHeader.links.community") }}
Community
</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in navigation.community" :key="item.name">
@@ -61,7 +54,7 @@
<div class="md:grid md:grid-cols-2 md:gap-8">
<div>
<h3 class="text-sm font-semibold leading-6 text-white">
{{ $t("footer.documentation") }}
Documentation
</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in navigation.documentation" :key="item.name">
@@ -74,9 +67,7 @@
</ul>
</div>
<div class="mt-10 md:mt-0">
<h3 class="text-sm font-semibold leading-6 text-white">
{{ $t("footer.about") }}
</h3>
<h3 class="text-sm font-semibold leading-6 text-white">About</h3>
<ul role="list" class="mt-6 space-y-4">
<li v-for="item in navigation.about" :key="item.name">
<NuxtLink
@@ -89,22 +80,6 @@
</div>
</div>
</div>
<div class="flex items-center justify-center xl:col-span-3 mt-8">
<NuxtLink
:to="`https://github.com/Drop-OSS/drop/releases/tag/${versionInfo.version}`"
class="text-xs text-zinc-700 hover:text-zinc-400 transition-colors duration-200 cursor-default select-none"
>
<i18n-t keypath="footer.version" tag="span" scope="global">
<template #version>
<span>{{ versionInfo.version }}</span>
</template>
<template #gitRef>
<span>{{ versionInfo.gitRef }}</span>
</template>
</i18n-t>
</NuxtLink>
</div>
</div>
</div>
</footer>
@@ -113,49 +88,45 @@
<script setup lang="ts">
import { IconsDiscordLogo, IconsGithubLogo } from "#components";
const { t } = useI18n();
const versionInfo = await $dropFetch("/api/v1");
const navigation = computed(() => ({
const navigation = {
games: [
{ name: t("store.recentlyAdded"), href: "#" },
{ name: t("store.recentlyReleased"), href: "#" },
{ name: t("footer.topSellers"), href: "#" },
{ name: t("footer.findGame"), href: "#" },
{ name: "Newly Added", href: "#" },
{ name: "New Releases", href: "#" },
{ name: "Top Sellers", href: "#" },
{ name: "Find a Game", href: "#" },
],
community: [
{ name: t("common.friends"), href: "#" },
{ name: t("common.groups"), href: "#" },
{ name: t("common.servers"), href: "#" },
{ name: "Friends", href: "#" },
{ name: "Groups", href: "#" },
{ name: "Servers", href: "#" },
],
documentation: [
// TODO: public API docs
// { name: t("footer.api"), href: "https://api.droposs.org/" },
{ name: "API", href: "https://api.droposs.org/" },
{
name: t("footer.docs.server"),
href: "https://docs.droposs.org/docs/guides/quickstart",
name: "Server Docs",
href: "https://wiki.droposs.org/guides/quickstart.html",
},
{
name: t("footer.docs.client"),
href: "https://docs.droposs.org/docs/guides/client",
name: "Client Docs",
href: "https://wiki.droposs.org/guides/client.html",
},
],
about: [
{ name: t("footer.aboutDrop"), href: "https://droposs.org/" },
{ name: t("footer.comparison"), href: "https://droposs.org/comparison" },
{ name: "About Drop", href: "https://droposs.org/" },
{ name: "Features", href: "https://droposs.org/features" },
{ name: "FAQ", href: "https://droposs.org/faq" },
],
social: [
{
name: t("footer.social.github"),
name: "GitHub",
href: "https://github.com/Drop-OSS",
icon: IconsGithubLogo,
},
{
name: t("footer.social.discord"),
name: "Discord",
href: "https://discord.gg/NHx46XKJWA",
icon: IconsDiscordLogo,
},
],
}));
};
</script>
@@ -76,7 +76,7 @@
class="-m-2.5 p-2.5 text-zinc-400 lg:hidden"
@click="sidebarOpen = true"
>
<span class="sr-only">{{ $t("header.openSidebar") }}</span>
<span class="sr-only">Open sidebar</span>
<Bars3Icon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
@@ -125,9 +125,7 @@
class="-m-2.5 p-2.5"
@click="sidebarOpen = false"
>
<span class="sr-only">{{
$t("userHeader.closeSidebar")
}}</span>
<span class="sr-only">Close sidebar</span>
<XMarkIcon class="h-6 w-6 text-zinc-400" aria-hidden="true" />
</button>
</div>
@@ -138,7 +136,7 @@
>
<div class="flex shrink-0 h-16 items-center justify-between">
<NuxtLink :to="homepageURL">
<ApplicationLogo class="h-8 w-auto" />
<DropLogo class="h-8 w-auto" />
</NuxtLink>
<UserHeaderUserWidget />
@@ -174,9 +172,6 @@
<BellIcon class="h-5" />
</UserHeaderWidget>
</li>
<li class="w-full">
<UserHeaderWidget class="w-full" />
</li>
</div>
</nav>
</div>
@@ -203,33 +198,32 @@ import { Bars3Icon } from "@heroicons/vue/24/outline";
import { XMarkIcon } from "@heroicons/vue/24/solid";
const router = useRouter();
const { t } = useI18n();
const homepageURL = "/store";
const navigation: Ref<Array<NavigationItem>> = computed(() => [
const navigation: Array<NavigationItem> = [
{
prefix: "/store",
route: "/store",
label: t("store.title"),
label: "Store",
},
{
prefix: "/library",
route: "/library",
label: t("userHeader.links.library"),
label: "Library",
},
{
prefix: "/community",
route: "/community",
label: t("userHeader.links.community"),
label: "Community",
},
{
prefix: "/news",
route: "/news",
label: t("userHeader.links.news"),
label: "News",
},
]);
];
const currentPageIndex = useCurrentNavigationIndex(navigation.value);
const currentPageIndex = useCurrentNavigationIndex(navigation);
const notifications = useNotifications();
const unreadNotifications = computed(() =>
@@ -6,7 +6,7 @@
>
<div class="ml-4 mt-2">
<h3 class="text-base font-semibold text-zinc-100 text-sm">
{{ $t("account.notifications.unread") }}
Unread notifications
</h3>
</div>
<div class="ml-4 mt-2 shrink-0">
@@ -15,15 +15,7 @@
type="button"
class="text-sm text-zinc-400"
>
<i18n-t
keypath="account.notifications.all"
tag="span"
scope="global"
>
<template #arrow>
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
</template>
</i18n-t>
View all &rarr;
</NuxtLink>
</div>
</div>
@@ -40,16 +32,13 @@
v-if="props.notifications.length == 0"
class="text-sm text-zinc-400 p-3 text-center w-full"
>
{{ $t("account.notifications.none") }}
No notifications
</div>
</PanelWidget>
</template>
<script setup lang="ts">
import type { SerializeObject } from "nitropack";
import type { NotificationModel } from "~/prisma/client/models";
import type { Notification } from "~/prisma/client";
const props = defineProps<{
notifications: Array<SerializeObject<NotificationModel>>;
}>();
const props = defineProps<{ notifications: Array<Notification> }>();
</script>
@@ -46,30 +46,29 @@
hydrate-on-visible
as="div"
>
<NuxtLink
:to="nav.route"
<!-- TODO: think this would work better as a NuxtLink instead of a button -->
<button
:href="nav.route"
:class="[
active ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400',
'w-full text-left transition block px-4 py-2 text-sm',
]"
@click="close"
@click="() => navigateTo(nav.route, close)"
>
{{ nav.label }}
</NuxtLink>
</button>
</MenuItem>
<MenuItem v-slot="{ active, close }" hydrate-on-visible as="div">
<NuxtLink
to="/auth/signout"
<MenuItem v-slot="{ active }" hydrate-on-visible as="div">
<!-- TODO: think this would work better as a NuxtLink instead of a button -->
<a
:class="[
active ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400',
'w-full text-left transition block px-4 py-2 text-sm',
]"
:data-comment="'external=true is required because we implemented the signout as a route on the server for performance'"
:external="true"
@click="close"
href="/auth/signout"
>
{{ $t("auth.signout") }}
</NuxtLink>
Signout
</a>
</MenuItem>
</div>
</PanelWidget>
@@ -86,25 +85,18 @@ import type { NavigationItem } from "~/composables/types";
const user = useUser();
const navigation = computed<NavigationItem[]>(() =>
[
user.value?.admin
? {
label: $t("userHeader.profile.admin"),
route: "/admin",
prefix: "",
}
: undefined,
{
label: $t("userHeader.profile.settings"),
route: "/account",
prefix: "",
},
{
label: "Authorize client",
route: "/client/code",
prefix: "",
},
].filter((e) => e !== undefined),
);
const navigation: NavigationItem[] = [
user.value?.admin
? {
label: "Admin Dashboard",
route: "/admin",
prefix: "",
}
: undefined,
{
label: "Account settings",
route: "/account",
prefix: "",
},
].filter((e) => e !== undefined);
</script>
@@ -1,12 +1,8 @@
import type {
CollectionModel,
CollectionEntryModel,
GameModel,
} from "~/prisma/client/models";
import type { Collection, CollectionEntry, Game } from "~/prisma/client";
import type { SerializeObject } from "nitropack";
type FullCollection = CollectionModel & {
entries: Array<CollectionEntryModel & { game: SerializeObject<GameModel> }>;
type FullCollection = Collection & {
entries: Array<CollectionEntry & { game: SerializeObject<Game> }>;
};
export const useCollections = async () => {
@@ -1,3 +1,4 @@
import type { RouteLocationNormalized } from "vue-router";
import type { NavigationItem } from "./types";
export const useCurrentNavigationIndex = (
@@ -8,7 +9,7 @@ export const useCurrentNavigationIndex = (
const currentNavigation = ref(-1);
function calculateCurrentNavIndex(to: typeof route) {
function calculateCurrentNavIndex(to: RouteLocationNormalized) {
const validOptions = navigation
.map((e, i) => ({ ...e, index: i }))
.filter((e) => to.fullPath.startsWith(e.prefix));
+8
View File
@@ -0,0 +1,8 @@
import { IconsLinuxLogo, IconsWindowsLogo, IconsMacLogo } from "#components";
import { PlatformClient } from "./types";
export const PLATFORM_ICONS = {
[PlatformClient.Linux]: IconsLinuxLogo,
[PlatformClient.Windows]: IconsWindowsLogo,
[PlatformClient.macOS]: IconsMacLogo,
};
@@ -1,11 +1,11 @@
import type { ArticleModel } from "~/prisma/client/models";
import type { Article } from "~/prisma/client";
import type { SerializeObject } from "nitropack";
export const useNews = () =>
useState<
| Array<
SerializeObject<
ArticleModel & {
Article & {
tags: Array<{ id: string; name: string }>;
author: { displayName: string; id: string } | null;
}
+12
View File
@@ -0,0 +1,12 @@
import type { Notification } from "~/prisma/client";
const ws = new WebSocketHandler("/api/v1/notifications/ws");
export const useNotifications = () =>
useState<Array<Notification>>("notifications", () => []);
ws.listen((e) => {
const notification = JSON.parse(e) as Notification;
const notifications = useNotifications();
notifications.value.push(notification);
});
+55
View File
@@ -0,0 +1,55 @@
import type {
ExtractedRouteMethod,
NitroFetchOptions,
NitroFetchRequest,
TypedInternalResponse,
} from "nitropack/types";
interface DropFetch<
DefaultT = unknown,
DefaultR extends NitroFetchRequest = NitroFetchRequest,
> {
<
T = DefaultT,
R extends NitroFetchRequest = DefaultR,
O extends NitroFetchOptions<R> = NitroFetchOptions<R>,
>(
request: R,
opts?: O,
): Promise<
// sometimes there is an error, other times there isn't
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
TypedInternalResponse<
R,
T,
NitroFetchOptions<R> extends O ? "get" : ExtractedRouteMethod<R, O>
>
>;
}
export const $dropFetch: DropFetch = async (request, opts) => {
if (!getCurrentInstance()?.proxy) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore Excessive stack depth comparing types
return await $fetch(request, opts);
}
const id = request.toString();
const state = useState(id);
if (state.value) {
// Deep copy
const object = JSON.parse(JSON.stringify(state.value));
// Never use again on client
state.value = undefined;
return object;
}
const headers = useRequestHeaders(["cookie", "authorization"]);
const data = await $fetch(request, {
...opts,
headers: { ...opts?.headers, ...headers },
});
if (import.meta.server) state.value = data;
return data;
};
@@ -52,7 +52,6 @@ websocketHandler.listen((message) => {
progress: 0,
error: undefined,
log: [],
actions: [],
};
state.value.error = { title, description };
break;
@@ -11,3 +11,9 @@ export type QuickActionNav = {
notifications?: Ref<number>;
action: () => Promise<void>;
};
export enum PlatformClient {
Windows = "Windows",
Linux = "Linux",
macOS = "macOS",
}
+13
View File
@@ -0,0 +1,13 @@
import type { User } from "~/prisma/client";
// undefined = haven't check
// null = check, no user
// {} = check, user
export const useUser = () => useState<User | undefined | null>(undefined);
export const updateUser = async () => {
const user = useUser();
if (user.value === null) return;
user.value = await $dropFetch<User | null>("/api/v1/user");
};
@@ -1,7 +1,8 @@
services:
postgres:
# using alpine image to reduce image size
image: postgres:alpine
image: postgres:14-alpine
ports:
- 5432:5432
healthcheck:
test: pg_isready -d drop -U drop
interval: 30s
@@ -15,10 +16,7 @@ services:
- POSTGRES_USER=drop
- POSTGRES_DB=drop
drop:
image: ghcr.io/drop-oss/drop:nightly
stdin_open: true
tty: true
init: true
image: registry.deepcore.dev/drop-oss/drop/main:latest
depends_on:
postgres:
condition: service_healthy
@@ -26,6 +24,11 @@ services:
- 3000:3000
volumes:
- ./library:/library
- ./data:/data
- ./certs:/certs
- ./objects:/objects
environment:
- DATABASE_URL=postgres://drop:drop@postgres:5432/drop
- FS_BACKEND_PATH=/objects
- CLIENT_CERTIFICATES=/certs
- LIBRARY=/library
- GIANT_BOMB_API_KEY=REPLACE_WITH_YOUR_KEY

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