From 5436678fed756451f87d409a976693697b8a3856 Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Tue, 20 Aug 2024 08:33:52 +0000 Subject: [PATCH] merge from local --- package.json | 3 - packages/app/aliases.js | 17 + packages/app/capacitor.config.json | 2 +- packages/app/package.json | 19 +- packages/app/src/App.jsx | 7 +- .../app/src/components/CoverEditor/index.jsx | 8 +- packages/app/src/components/Login/index.jsx | 7 +- .../MusicStudio/ReleaseEditor/index.jsx | 147 ++++--- .../tabs/BasicInformation/index.jsx | 20 +- .../Tracks/components/TrackListItem/index.jsx | 89 +++++ .../components/TrackListItem/index.less | 45 +++ .../Tracks/components/UploadHint/index.jsx | 13 + .../Tracks/components/UploadHint/index.less | 4 + .../ReleaseEditor/tabs/Tracks/index.jsx | 333 ++++++++++------ .../ReleaseEditor/tabs/Tracks/index.less | 46 --- packages/app/src/components/Poll/index.jsx | 91 ++++- packages/app/src/components/Poll/index.less | 44 ++- .../app/src/components/PostCreator/index.jsx | 27 ++ .../src/contexts/MusicReleaseEditor/index.js | 15 + .../app/src/hooks/useHideToolsBar/index.js | 19 + .../app/src/hooks/useLayoutInterface/index.js | 2 +- .../{ => @mobile}/bottomBar/index.jsx | 2 +- .../{ => @mobile}/bottomBar/index.less | 0 .../{ => @mobile}/quickNav/index.jsx | 0 .../components/{ => @mobile}/topBar/index.jsx | 0 .../{ => @mobile}/topBar/index.less | 0 .../components/draggableDrawer/index.jsx | 66 ++++ .../src/layouts/components/drawer/index.jsx | 365 +++++++++++------- .../src/layouts/components/drawer/index.less | 66 ++++ .../src/layouts/components/modals/index.jsx | 121 +----- .../src/layouts/components/modals/index.less | 117 ------ .../layouts/components/modals/modal/index.jsx | 110 ++++++ .../components/modals/modal/index.less | 117 ++++++ .../src/layouts/components/sidebar/index.jsx | 90 ++++- .../src/layouts/components/sidebar/index.less | 14 +- .../layouts/components/sidedrawer/index.jsx | 262 ------------- .../layouts/components/sidedrawer/index.less | 48 --- packages/app/src/layouts/default/index.jsx | 18 +- packages/app/src/layouts/minimal/index.jsx | 5 +- packages/app/src/pages/auth/index.mobile.jsx | 23 +- .../lyrics/components/controller/index.jsx | 4 +- packages/app/src/pages/lyrics/index.jsx | 20 +- packages/app/src/pages/lyrics/index.less | 33 ++ .../src/pages/messages/[to_user_id]/index.jsx | 1 - packages/app/src/pages/messages/index.jsx | 1 - packages/app/src/pages/nfc/[tag_id].jsx | 7 + packages/app/src/settings/about/index.jsx | 48 +-- packages/app/src/settings/api/index.jsx | 81 ++++ packages/app/src/settings/api/index.less | 32 ++ packages/app/src/settings/apparence/index.jsx | 5 +- .../app/src/settings/changelogs/index.jsx | 2 +- .../components/changePassword/index.jsx | 6 +- .../themeVariantSelector/index.less | 2 + packages/app/src/settings/general/index.jsx | 8 +- packages/app/src/settings/tap_share/index.jsx | 24 +- .../tap_share/steps/data_editor/index.jsx | 4 +- packages/app/src/styles/index.less | 13 + packages/app/vite.config.js | 22 +- packages/server/.dockerignore | 8 + packages/server/.swcrc | 15 + packages/server/Dockerfile | 45 ++- packages/server/docker-compose.yml | 11 + packages/server/gateway/index.js | 2 +- packages/server/gateway/proxy.js | 2 +- packages/server/package.json | 86 +++-- packages/server/services/auth/auth.service.js | 2 +- packages/server/services/auth/package.json | 8 +- .../server/services/auth/routes/auth/post.js | 7 +- .../auth/routes/server-keys/my/get.js | 7 + .../server/services/chats/chats.service.js | 2 +- packages/server/services/chats/package.json | 23 +- .../routes/chats/[chat_id]/history/get.js | 2 +- .../chats/routes_ws/chat/send/message.js | 2 - packages/server/services/ems/ems.service.js | 2 +- packages/server/services/ems/package.json | 6 - .../server/services/files/file.service.js | 2 +- packages/server/services/files/package.json | 16 - .../files/routes/upload/chunk/post.js | 7 + .../endpoints/accounts_statistics.js | 58 --- .../endpoints/delete_featured_wallpaper.js | 26 -- .../endpoints/featured_wallpaper.js | 39 -- .../endpoints/resetPassword.js | 36 -- .../endpoints/update_user_data.js | 35 -- .../main/controllers/AdminController/index.js | 9 - .../main/controllers/AutoUpdate/index.js | 46 --- .../BadgesController/endpoints/deleteBadge.js | 21 - .../BadgesController/endpoints/getBadges.js | 29 -- .../endpoints/getUserBadges.js | 21 - .../BadgesController/endpoints/giveToUser.js | 41 -- .../BadgesController/endpoints/putBadge.js | 27 -- .../endpoints/removeToUser.js | 41 -- .../controllers/BadgesController/index.js | 9 - .../FeaturedEventsController/index.js | 63 --- .../services/createFeaturedEvent.js | 27 -- .../NFCController/endpoints/getExecution.js | 11 - .../main/controllers/NFCController/index.js | 9 - .../endpoints/featuredWallpapers.js | 28 -- .../endpoints/getReleasesNotes.js | 49 --- .../endpoints/globalServerLimits.js | 27 -- .../PublicController/endpoints/ping.js | 7 - .../endpoints/postingPolicy.js | 9 - .../endpoints/serverHealth.js | 29 -- .../PublicController/incidentPrediction.js | 27 -- .../controllers/PublicController/index.js | 8 - .../endpoints/getQuickSearch.js | 20 - .../SearchController/endpoints/getSearch.js | 86 ----- .../controllers/SearchController/index.js | 9 - .../endpoints/getConnectedFollowedUsers.js | 16 - .../controllers/StatusController/index.js | 9 - .../services/getConnectedUsersFollowing.js | 28 -- .../server/services/main/controllers/index.js | 25 -- .../main/fixments/additions_to_attachments.js | 26 -- .../main/fixments/migrate_posts_likes.js | 96 ----- .../main/fixments/move_playlist_to_release.js | 47 --- .../services/main/lib/checkUserAdmin/index.js | 19 - .../services/main/lib/chunkedUpload/index.js | 232 ----------- packages/server/services/main/lib/index.js | 3 - .../services/main/lib/schematized/index.js | 66 ---- .../services/main/lib/secureEntry/index.js | 133 ------- .../server/services/main/lib/token/index.js | 223 ----------- .../services/main/lib/videoTranscode/index.js | 43 --- packages/server/services/main/main.service.js | 2 +- packages/server/services/main/package.json | 27 +- .../nfc/tag/id/[id]/delete.js} | 17 +- .../main/routes/nfc/tag/id/[id]/get.js | 27 ++ .../getTags.js => routes/nfc/tag/my/get.js} | 6 +- .../nfc/tag/register/[serial]/post.js} | 12 +- .../nfc/tag/serial/get.js} | 6 +- .../routes/repo/auto-update/mobile/get.js | 31 ++ .../main/routes/repo/releases-notes/get.js | 45 +++ .../main/services/fetchRemoteStreams/index.js | 78 ---- .../main/services/getMutuals/index.js | 30 -- .../services/newStreamingProfile/index.js | 28 -- .../main/utils/aggregate-error/index.js | 45 --- .../services/main/utils/clean-stack/index.js | 51 --- .../utils/compose-streaming-sources/index.js | 16 - .../utils/createTokenRegeneration/index.js | 17 - .../main/utils/download-file/index.js | 39 -- .../main/utils/escape-string-regexp/index.js | 12 - .../main/utils/fullfillPostsData/index.js | 94 ----- .../main/utils/image-byte-array/index.js | 14 - .../main/utils/indecent-prediction/index.js | 73 ---- .../main/utils/indent-string/index.js | 39 -- .../services/main/utils/is-nsfw/index.js | 22 -- .../server/services/main/utils/pMap/index.js | 198 ---------- .../services/main/utils/read-image/index.js | 9 - .../marketplace/marketplace.service.js | 2 +- .../server/services/marketplace/package.json | 28 -- .../music/classes/track/methods/create.js | 2 +- .../controllers/playlists/routes/get/self.js | 4 +- .../controllers/releases/routes/get/self.js | 2 +- .../releases/routes/get/user/[user_id].js | 2 +- .../controllers/tracks/routes/get/liked.js | 2 +- .../music/getPlaylistsFromFollowing.js | 42 -- .../music/getReleasesFromFollowing.js | 40 -- .../server/services/music/music.service.js | 2 +- packages/server/services/music/package.json | 26 +- .../services/music/routes/music/feed/get.js | 2 +- .../music/routes/music/releases/self/get.js | 2 +- .../services/music/services/findSpotifyId.js | 45 --- .../services/getEnhancedLyricsFromTrack.js | 73 ---- .../services/music/services/removeTracks.js | 49 --- packages/server/services/music/ws.js | 117 ------ .../notifications/notifications.service.js | 2 +- .../services/notifications/package.json | 4 +- .../posts/classes/posts/methods/fullfill.js | 2 +- .../posts/classes/posts/methods/toggleLike.js | 2 +- packages/server/services/posts/package.json | 6 - .../server/services/posts/posts.service.js | 2 +- .../services/sync/classes/VRCApi/index.js | 59 --- .../services/sync/controllers/main/index.js | 11 - .../main/routes/get/active_services.js | 32 -- .../sync/controllers/services/index.js | 11 - .../delete/tidal/track/[track_id]/like.js | 34 -- .../services/routes/get/tidal/create_link.js | 47 --- .../services/routes/get/tidal/current.js | 22 -- .../routes/get/tidal/favorites/playlists.js | 34 -- .../routes/get/tidal/favorites/tracks.js | 34 -- .../services/routes/get/tidal/is_active.js | 18 - .../routes/get/tidal/manifest/[track_id].js | 36 -- .../routes/get/tidal/playback/[track_id].js | 27 -- .../routes/get/tidal/playlist/[uuid]/data.js | 35 -- .../routes/get/tidal/playlist/[uuid]/items.js | 34 -- .../services/routes/get/tidal/search.js | 11 - .../services/routes/get/vrc/session.js | 12 - .../services/routes/post/tidal/delete_link.js | 16 - .../post/tidal/track/[track_id]/like.js | 35 -- .../services/routes/post/vrc/auth.js | 57 --- .../services/routes/post/vrc/logout.js | 0 .../services/routes/post/vrc/otp.js | 45 --- .../sync/middlewares/withAuth/index.js | 25 -- .../middlewares/withOptionalAuth/index.js | 26 -- .../services/sync/middlewares/withWsAuth.js | 55 --- packages/server/services/sync/package.json | 25 -- packages/server/services/sync/sync.service.js | 144 ------- .../sync/useMiddlewares/useAuth/index.js | 25 -- .../sync/useMiddlewares/useCors/index.js | 8 - .../sync/useMiddlewares/useLogger/index.js | 14 - .../sync/utils/composePayloadData/index.js | 12 - .../utils/createRoutesFromDirectory/index.js | 45 --- .../sync/utils/generateFnHandler/index.js | 21 - .../sync/utils/getMiddlewares/index.js | 46 --- .../services/sync/utils/resolveUrl/index.js | 20 - .../classes/users/method/getFollowers.js | 2 +- .../classes/users/method/toggleFollow.js | 2 +- packages/server/services/users/package.json | 4 +- .../server/services/users/users.service.js | 2 +- 207 files changed, 1898 insertions(+), 5347 deletions(-) create mode 100644 packages/app/aliases.js create mode 100644 packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx create mode 100644 packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.less create mode 100644 packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.jsx create mode 100644 packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.less create mode 100644 packages/app/src/contexts/MusicReleaseEditor/index.js create mode 100644 packages/app/src/hooks/useHideToolsBar/index.js rename packages/app/src/layouts/components/{ => @mobile}/bottomBar/index.jsx (99%) rename packages/app/src/layouts/components/{ => @mobile}/bottomBar/index.less (100%) rename packages/app/src/layouts/components/{ => @mobile}/quickNav/index.jsx (100%) rename packages/app/src/layouts/components/{ => @mobile}/topBar/index.jsx (100%) rename packages/app/src/layouts/components/{ => @mobile}/topBar/index.less (100%) create mode 100644 packages/app/src/layouts/components/drawer/index.less create mode 100644 packages/app/src/layouts/components/modals/modal/index.jsx create mode 100644 packages/app/src/layouts/components/modals/modal/index.less delete mode 100755 packages/app/src/layouts/components/sidedrawer/index.jsx delete mode 100755 packages/app/src/layouts/components/sidedrawer/index.less create mode 100644 packages/app/src/settings/api/index.jsx create mode 100644 packages/app/src/settings/api/index.less create mode 100644 packages/server/.dockerignore create mode 100644 packages/server/.swcrc create mode 100644 packages/server/docker-compose.yml create mode 100644 packages/server/services/auth/routes/server-keys/my/get.js delete mode 100755 packages/server/services/main/controllers/AdminController/endpoints/accounts_statistics.js delete mode 100755 packages/server/services/main/controllers/AdminController/endpoints/delete_featured_wallpaper.js delete mode 100755 packages/server/services/main/controllers/AdminController/endpoints/featured_wallpaper.js delete mode 100755 packages/server/services/main/controllers/AdminController/endpoints/resetPassword.js delete mode 100755 packages/server/services/main/controllers/AdminController/endpoints/update_user_data.js delete mode 100755 packages/server/services/main/controllers/AdminController/index.js delete mode 100755 packages/server/services/main/controllers/AutoUpdate/index.js delete mode 100755 packages/server/services/main/controllers/BadgesController/endpoints/deleteBadge.js delete mode 100755 packages/server/services/main/controllers/BadgesController/endpoints/getBadges.js delete mode 100755 packages/server/services/main/controllers/BadgesController/endpoints/getUserBadges.js delete mode 100755 packages/server/services/main/controllers/BadgesController/endpoints/giveToUser.js delete mode 100755 packages/server/services/main/controllers/BadgesController/endpoints/putBadge.js delete mode 100755 packages/server/services/main/controllers/BadgesController/endpoints/removeToUser.js delete mode 100755 packages/server/services/main/controllers/BadgesController/index.js delete mode 100755 packages/server/services/main/controllers/FeaturedEventsController/index.js delete mode 100755 packages/server/services/main/controllers/FeaturedEventsController/services/createFeaturedEvent.js delete mode 100755 packages/server/services/main/controllers/NFCController/endpoints/getExecution.js delete mode 100755 packages/server/services/main/controllers/NFCController/index.js delete mode 100755 packages/server/services/main/controllers/PublicController/endpoints/featuredWallpapers.js delete mode 100755 packages/server/services/main/controllers/PublicController/endpoints/getReleasesNotes.js delete mode 100755 packages/server/services/main/controllers/PublicController/endpoints/globalServerLimits.js delete mode 100755 packages/server/services/main/controllers/PublicController/endpoints/ping.js delete mode 100755 packages/server/services/main/controllers/PublicController/endpoints/postingPolicy.js delete mode 100755 packages/server/services/main/controllers/PublicController/endpoints/serverHealth.js delete mode 100755 packages/server/services/main/controllers/PublicController/incidentPrediction.js delete mode 100755 packages/server/services/main/controllers/PublicController/index.js delete mode 100755 packages/server/services/main/controllers/SearchController/endpoints/getQuickSearch.js delete mode 100755 packages/server/services/main/controllers/SearchController/endpoints/getSearch.js delete mode 100755 packages/server/services/main/controllers/SearchController/index.js delete mode 100755 packages/server/services/main/controllers/StatusController/endpoints/getConnectedFollowedUsers.js delete mode 100755 packages/server/services/main/controllers/StatusController/index.js delete mode 100755 packages/server/services/main/controllers/StatusController/services/getConnectedUsersFollowing.js delete mode 100755 packages/server/services/main/controllers/index.js delete mode 100755 packages/server/services/main/fixments/additions_to_attachments.js delete mode 100755 packages/server/services/main/fixments/migrate_posts_likes.js delete mode 100755 packages/server/services/main/fixments/move_playlist_to_release.js delete mode 100755 packages/server/services/main/lib/checkUserAdmin/index.js delete mode 100755 packages/server/services/main/lib/chunkedUpload/index.js delete mode 100755 packages/server/services/main/lib/index.js delete mode 100755 packages/server/services/main/lib/schematized/index.js delete mode 100755 packages/server/services/main/lib/secureEntry/index.js delete mode 100755 packages/server/services/main/lib/token/index.js delete mode 100755 packages/server/services/main/lib/videoTranscode/index.js rename packages/server/services/main/{controllers/NFCController/endpoints/getTagById.js => routes/nfc/tag/id/[id]/delete.js} (55%) mode change 100755 => 100644 create mode 100644 packages/server/services/main/routes/nfc/tag/id/[id]/get.js rename packages/server/services/main/{controllers/NFCController/endpoints/getTags.js => routes/nfc/tag/my/get.js} (64%) mode change 100755 => 100644 rename packages/server/services/main/{controllers/NFCController/endpoints/registerTag.js => routes/nfc/tag/register/[serial]/post.js} (85%) mode change 100755 => 100644 rename packages/server/services/main/{controllers/NFCController/endpoints/getTagBySerial.js => routes/nfc/tag/serial/get.js} (78%) mode change 100755 => 100644 create mode 100644 packages/server/services/main/routes/repo/auto-update/mobile/get.js create mode 100644 packages/server/services/main/routes/repo/releases-notes/get.js delete mode 100755 packages/server/services/main/services/fetchRemoteStreams/index.js delete mode 100755 packages/server/services/main/services/getMutuals/index.js delete mode 100755 packages/server/services/main/services/newStreamingProfile/index.js delete mode 100755 packages/server/services/main/utils/aggregate-error/index.js delete mode 100755 packages/server/services/main/utils/clean-stack/index.js delete mode 100755 packages/server/services/main/utils/compose-streaming-sources/index.js delete mode 100755 packages/server/services/main/utils/createTokenRegeneration/index.js delete mode 100755 packages/server/services/main/utils/download-file/index.js delete mode 100755 packages/server/services/main/utils/escape-string-regexp/index.js delete mode 100755 packages/server/services/main/utils/fullfillPostsData/index.js delete mode 100755 packages/server/services/main/utils/image-byte-array/index.js delete mode 100755 packages/server/services/main/utils/indecent-prediction/index.js delete mode 100755 packages/server/services/main/utils/indent-string/index.js delete mode 100755 packages/server/services/main/utils/is-nsfw/index.js delete mode 100755 packages/server/services/main/utils/pMap/index.js delete mode 100755 packages/server/services/main/utils/read-image/index.js delete mode 100755 packages/server/services/music/getPlaylistsFromFollowing.js delete mode 100755 packages/server/services/music/getReleasesFromFollowing.js delete mode 100755 packages/server/services/music/services/findSpotifyId.js delete mode 100755 packages/server/services/music/services/getEnhancedLyricsFromTrack.js delete mode 100755 packages/server/services/music/services/removeTracks.js delete mode 100755 packages/server/services/music/ws.js delete mode 100755 packages/server/services/sync/classes/VRCApi/index.js delete mode 100755 packages/server/services/sync/controllers/main/index.js delete mode 100755 packages/server/services/sync/controllers/main/routes/get/active_services.js delete mode 100755 packages/server/services/sync/controllers/services/index.js delete mode 100755 packages/server/services/sync/controllers/services/routes/delete/tidal/track/[track_id]/like.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/create_link.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/current.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/favorites/playlists.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/favorites/tracks.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/is_active.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/manifest/[track_id].js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/playback/[track_id].js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/data.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/items.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/tidal/search.js delete mode 100755 packages/server/services/sync/controllers/services/routes/get/vrc/session.js delete mode 100755 packages/server/services/sync/controllers/services/routes/post/tidal/delete_link.js delete mode 100755 packages/server/services/sync/controllers/services/routes/post/tidal/track/[track_id]/like.js delete mode 100755 packages/server/services/sync/controllers/services/routes/post/vrc/auth.js delete mode 100755 packages/server/services/sync/controllers/services/routes/post/vrc/logout.js delete mode 100755 packages/server/services/sync/controllers/services/routes/post/vrc/otp.js delete mode 100755 packages/server/services/sync/middlewares/withAuth/index.js delete mode 100755 packages/server/services/sync/middlewares/withOptionalAuth/index.js delete mode 100755 packages/server/services/sync/middlewares/withWsAuth.js delete mode 100755 packages/server/services/sync/package.json delete mode 100755 packages/server/services/sync/sync.service.js delete mode 100755 packages/server/services/sync/useMiddlewares/useAuth/index.js delete mode 100755 packages/server/services/sync/useMiddlewares/useCors/index.js delete mode 100755 packages/server/services/sync/useMiddlewares/useLogger/index.js delete mode 100755 packages/server/services/sync/utils/composePayloadData/index.js delete mode 100755 packages/server/services/sync/utils/createRoutesFromDirectory/index.js delete mode 100755 packages/server/services/sync/utils/generateFnHandler/index.js delete mode 100755 packages/server/services/sync/utils/getMiddlewares/index.js delete mode 100755 packages/server/services/sync/utils/resolveUrl/index.js diff --git a/package.json b/package.json index d766968d..14f212eb 100755 --- a/package.json +++ b/package.json @@ -12,9 +12,6 @@ "postdeploy": "node ./scripts/post-deploy.js", "postinstall": "node ./scripts/post-install.js" }, - "workspaces": [ - "packages/**" - ], "dependencies": { "7zip-min": "1.4.3", "axios": "^1.4.0", diff --git a/packages/app/aliases.js b/packages/app/aliases.js new file mode 100644 index 00000000..19d1f1be --- /dev/null +++ b/packages/app/aliases.js @@ -0,0 +1,17 @@ +import path from "path" + +export default { + "@": path.join(__dirname, "src"), + "@config": path.join(__dirname, "config"), + "@cores": path.join(__dirname, "src/cores"), + "@pages": path.join(__dirname, "src/pages"), + "@styles": path.join(__dirname, "src/styles"), + "@components": path.join(__dirname, "src/components"), + "@contexts": path.join(__dirname, "src/contexts"), + "@utils": path.join(__dirname, "src/utils"), + "@layouts": path.join(__dirname, "src/layouts"), + "@hooks": path.join(__dirname, "src/hooks"), + "@classes": path.join(__dirname, "src/classes"), + "@models": path.join(__dirname, "../../", "comty.js/src/models"), + "comty.js": path.join(__dirname, "../../", "comty.js", "src"), +} \ No newline at end of file diff --git a/packages/app/capacitor.config.json b/packages/app/capacitor.config.json index 9e63b8d8..bee2ad19 100755 --- a/packages/app/capacitor.config.json +++ b/packages/app/capacitor.config.json @@ -5,7 +5,7 @@ "webDir": "dist", "plugins": { "CapacitorUpdater": { - "updateUrl": "https://api.comty.app/auto-update/mobile", + "updateUrl": "https://api.comty.app/repo/auto-update/mobile", "resetWhenUpdate": true } } diff --git a/packages/app/package.json b/packages/app/package.json index 85255a9d..612a4ec3 100755 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,6 +8,7 @@ "scripts": { "build": "vite build", "dev": "vite", + "dev:farm": "farm start", "docker-compose:update_run": "docker-compose down && git pull && yarn build && docker-compose up -d --build", "preview": "vite preview" }, @@ -15,7 +16,7 @@ "react": "^18.2.0" }, "dependencies": { - "@ant-design/icons": "4.7.0", + "@ant-design/icons": "^5.4.0", "@capacitor/android": "^5.0.5", "@capacitor/app": "^5.0.3", "@capacitor/cli": "^5.0.5", @@ -30,16 +31,24 @@ "@dnd-kit/core": "^6.0.8", "@dnd-kit/sortable": "^7.0.2", "@emotion/css": "11.0.0", + "@emotion/react": "^11.13.0", + "@emotion/styled": "^11.13.0", + "@farmfe/cli": "^1.0.2", + "@farmfe/core": "^1.2.6", + "@farmfe/js-plugin-less": "^1.8.0", + "@farmfe/plugin-react": "^1.1.0", "@ffmpeg/ffmpeg": "^0.12.10", "@ffmpeg/util": "^0.12.1", "@loadable/component": "5.15.2", "@mui/material": "^5.11.9", "@ragestudio/cordova-nfc": "^1.2.0", "@sentry/browser": "^7.64.0", + "@stripe/react-stripe-js": "^2.7.3", + "@stripe/stripe-js": "^4.2.0", "@tanstack/react-virtual": "^3.5.0", "@tauri-apps/api": "^1.5.4", "@tsmx/human-readable": "^1.0.7", - "antd": "^5.17.0", + "antd": "^5.20.1", "antd-mobile": "^5.31.0", "axios": "^1.6.8", "bear-react-carousel": "^4.0.10-alpha.0", @@ -58,9 +67,7 @@ "js-cookie": "3.0.1", "jsmediatags": "^3.9.7", "less": "4.1.2", - "linebridge": "0.16.0", "lottie-react": "^2.4.0", - "lru-cache": "^10.0.0", "luxon": "^3.0.4", "million": "^2.6.4", "mime": "^3.0.0", @@ -71,9 +78,10 @@ "plyr": "^3.6.12", "plyr-react": "^3.2.1", "prop-types": "^15.8.1", - "react": "18.2.0", + "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", "react-color": "2.19.3", + "react-confetti-explosion": "^2.1.2", "react-countup": "^6.4.1", "react-dom": "18.2.0", "react-fast-marquee": "^1.3.5", @@ -87,6 +95,7 @@ "react-motion": "0.5.2", "react-rnd": "10.3.5", "react-router-dom": "^6.6.2", + "react-slot-counter": "^3.0.1", "react-ticker": "^1.3.2", "react-transition-group": "^4.4.5", "react-useanimations": "^2.10.0", diff --git a/packages/app/src/App.jsx b/packages/app/src/App.jsx index 214683da..8f48bf83 100755 --- a/packages/app/src/App.jsx +++ b/packages/app/src/App.jsx @@ -106,7 +106,7 @@ class ComtyApp extends React.Component { } }, openLoginForm: async (options = {}) => { - app.layout.drawer.open("login", Login, { + app.layout.draggable.open("login", Login, { defaultLocked: options.defaultLocked ?? false, componentProps: { sessionController: this.sessionController, @@ -134,7 +134,7 @@ class ComtyApp extends React.Component { }, // Opens the notification window and sets up the UI for the notification to be displayed openNotifications: () => { - window.app.layout.sidedrawer.open("notifications", NotificationsCenter, { + window.app.layout.drawer.open("notifications", NotificationsCenter, { props: { width: "fit-content", }, @@ -158,7 +158,7 @@ class ComtyApp extends React.Component { }) }, openMessages: () => { - app.location.push("/messages") + app.location.push("/messages") }, openFullImageViewer: (src) => { app.cores.window_mng.render("image_lightbox", ) }, - openPostCreator: (params) => { app.layout.modal.open("post_creator", (props) => { const [url, setUrl] = React.useState(value) - React.useEffect(() => { - setUrl(value) - }, [value]) - React.useEffect(() => { onChange(url) }, [url]) React.useEffect(() => { - if (!url) { + if (!value) { setUrl(defaultUrl) + } else { + setUrl(value) } }, []) diff --git a/packages/app/src/components/Login/index.jsx b/packages/app/src/components/Login/index.jsx index a46436d8..603212df 100755 --- a/packages/app/src/components/Login/index.jsx +++ b/packages/app/src/components/Login/index.jsx @@ -304,9 +304,10 @@ class Login extends React.Component { } - this.onUpdateInput("mfa_code", e.target.value)} + str.toUpperCase()} + onChange={(code) => this.onUpdateInput("mfa_code", code)} onPressEnter={this.nextStep} /> diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx index ea5b1628..68405fdd 100644 --- a/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx @@ -5,6 +5,8 @@ import { Icons } from "@components/Icons" import MusicModel from "@models/music" +import { DefaultReleaseEditorState, ReleaseEditorStateContext } from "@contexts/MusicReleaseEditor" + import Tabs from "./tabs" import "./index.less" @@ -14,11 +16,33 @@ const ReleaseEditor = (props) => { const basicInfoRef = React.useRef() + const [loading, setLoading] = React.useState(true) + const [loadError, setLoadError] = React.useState(null) + const [globalState, setGlobalState] = React.useState(DefaultReleaseEditorState) const [selectedTab, setSelectedTab] = React.useState("info") - const [L_Release, R_Release, E_Release, F_Release] = release_id !== "new" ? app.cores.api.useRequest(MusicModel.getReleaseData, release_id) : [false, false, false, false] + + async function initialize() { + setLoading(true) + setLoadError(null) + + if (release_id !== "new") { + try { + const releaseData = await MusicModel.getReleaseData(release_id) + + setGlobalState({ + ...globalState, + ...releaseData, + }) + } catch (error) { + setLoadError(error) + } + } + + setLoading(false) + } async function handleSubmit() { - basicInfoRef.current.submit() + console.log("Submit >", globalState) } async function onFinish(values) { @@ -29,79 +53,88 @@ const ReleaseEditor = (props) => { return true } - if (E_Release) { + React.useEffect(() => { + initialize() + }, []) + + if (loadError) { return } - if (L_Release) { + if (loading) { return } const Tab = Tabs.find(({ key }) => key === selectedTab) - return
-
- setSelectedTab(e.key)} - selectedKeys={[selectedTab]} - items={Tabs} - mode="vertical" - /> + return +
+
+ setSelectedTab(e.key)} + selectedKeys={[selectedTab]} + items={Tabs} + mode="vertical" + /> -
- } - disabled={L_Release || !canFinish()} - > - Save - - - { - release_id !== "new" ? } - disabled={L_Release} +
+ } + disabled={loading || !canFinish()} > - Delete - : null + Save + + + { + release_id !== "new" ? } + disabled={loading} + > + Delete + : null + } + + { + release_id !== "new" ? } + onClick={() => app.location.push(`/music/release/${globalState._id}`)} + > + Go to release + : null + } +
+
+ +
+ { + !Tab && } - { - release_id !== "new" ? } - onClick={() => app.location.push(`/music/release/${R_Release._id}`)} - > - Go to release - : null + Tab && React.createElement(Tab.render, { + release: globalState, + onFinish: onFinish, + + state: globalState, + setState: setGlobalState, + + references: { + basic: basicInfoRef + } + }) }
- -
- { - !Tab && - } - { - Tab && React.createElement(Tab.render, { - release: R_Release, - onFinish: onFinish, - - references: { - basic: basicInfoRef - } - }) - } -
-
+
} export default ReleaseEditor \ No newline at end of file diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/BasicInformation/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/BasicInformation/index.jsx index af4205dd..78d64114 100644 --- a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/BasicInformation/index.jsx +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/BasicInformation/index.jsx @@ -29,7 +29,16 @@ const ReleasesTypes = [ ] const BasicInformation = (props) => { - const { release, onFinish } = props + const { release, onFinish, setState, state } = props + + async function onFormChange(change) { + setState((globalState) => { + return { + ...globalState, + ...change + } + }) + } return

Release Information

@@ -40,12 +49,13 @@ const BasicInformation = (props) => { ref={props.references.basic} onFinish={onFinish} requiredMark={false} + onValuesChange={onFormChange} > { label={<> Title} name="title" rules={[{ required: true, message: "Input a title for the release" }]} - initialValue={release?.title} + initialValue={state?.title} > { label={<> Type} name="type" rules={[{ required: true, message: "Select a type for the release" }]} - initialValue={release?.type} + initialValue={state?.type} > { Public} name="public" - initialValue={release?.public} + initialValue={state?.public} > diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx new file mode 100644 index 00000000..c2d8b095 --- /dev/null +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx @@ -0,0 +1,89 @@ +import React from "react" +import * as antd from "antd" +import classnames from "classnames" +import { Draggable } from "react-beautiful-dnd" + +import Image from "@components/Image" +import { Icons } from "@components/Icons" +import TrackEditor from "@components/MusicStudio/TrackEditor" + +import "./index.less" + +const TrackListItem = (props) => { + const [loading, setLoading] = React.useState(false) + const [error, setError] = React.useState(null) + + const { track } = props + + async function onClickEditTrack() { + app.layout.drawer.open("track_editor", TrackEditor, { + type: "drawer", + props: { + width: "600px", + headerStyle: { + display: "none", + } + }, + componentProps: { + track, + onSave: (newTrackData) => { + console.log("Saving track", newTrackData) + }, + }, + }) + } + + return + { + (provided, snapshot) => { + return
+
+ {props.index + 1} +
+ + + + {track.title} + +
+ } + onClick={onClickEditTrack} + /> + +
+ +
+
+
+ } + } +
+} + +export default TrackListItem \ No newline at end of file diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.less b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.less new file mode 100644 index 00000000..70989cea --- /dev/null +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.less @@ -0,0 +1,45 @@ +.music-studio-release-editor-tracks-list-item { + position: relative; + + display: flex; + flex-direction: row; + + padding: 10px; + + gap: 10px; + + border-radius: 12px; + + background-color: var(--background-color-accent); + + .music-studio-release-editor-tracks-list-item-actions { + position: absolute; + + top: 0; + right: 0; + + display: flex; + + align-items: center; + justify-content: center; + + height: 100%; + + padding: 0 5px; + + svg { + margin: 0; + } + + .music-studio-release-editor-tracks-list-item-dragger { + display: flex; + + align-items: center; + justify-content: center; + + svg { + font-size: 1rem; + } + } + } +} \ No newline at end of file diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.jsx new file mode 100644 index 00000000..4d75fb75 --- /dev/null +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.jsx @@ -0,0 +1,13 @@ +import React from "react" +import { Icons } from "@components/Icons" + +import "./index.less" + +export default (props) => { + return
+ + +

Upload your tracks

+

Drag and drop your tracks here or click this box to start uploading files.

+
+} \ No newline at end of file diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.less b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.less new file mode 100644 index 00000000..8f108549 --- /dev/null +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/UploadHint/index.less @@ -0,0 +1,4 @@ +.music-studio-tracks-uploader-hint { + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.jsx index 2ccebcf0..8b639e72 100644 --- a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.jsx +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.jsx @@ -1,163 +1,221 @@ import React from "react" import * as antd from "antd" import classnames from "classnames" -import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd" +import { DragDropContext, Droppable } from "react-beautiful-dnd" +import jsmediatags from "jsmediatags/dist/jsmediatags.min.js" import { Icons } from "@components/Icons" -import TrackEditor from "@components/MusicStudio/TrackEditor" + +import TrackListItem from "./components/TrackListItem" +import UploadHint from "./components/UploadHint" import "./index.less" -const UploadHint = (props) => { - return
- -

Upload your tracks

-

Drag and drop your tracks here or click this box to start uploading files.

-
+async function uploadBinaryArrayToStorage(bin, args) { + const { format, data } = bin + + const filenameExt = format.split("/")[1] + const filename = `cover.${filenameExt}` + + const byteArray = new Uint8Array(data) + const blob = new Blob([byteArray], { type: data.type }) + + // create a file object + const file = new File([blob], filename, { + type: format, + }) + + return await app.cores.remoteStorage.uploadFile(file, args) } -const TrackListItem = (props) => { - const [loading, setLoading] = React.useState(false) - const [error, setError] = React.useState(null) +class TrackManifest { + constructor(params) { + this.params = params - const { track } = props + return this + } - async function onClickEditTrack() { - app.layout.drawer.open("track_editor", TrackEditor, { - type: "drawer", - props: { - width: "600px", - headerStyle: { - display: "none", - } - }, - componentProps: { - track, - onSave: (newTrackData) => { - console.log("Saving track", newTrackData) + cover = "https://storage.ragestudio.net/comty-static-assets/default_song.png" + + title = "Untitled" + + album = "Unknown" + + artist = "Unknown" + + source = null + + async initialize() { + const metadata = await this.analyzeMetadata(this.params.file.originFileObj) + + console.log(metadata) + + if (metadata.tags) { + if (metadata.tags.title) { + this.title = metadata.tags.title + } + + if (metadata.tags.artist) { + this.artist = metadata.tags.artist + } + + if (metadata.tags.album) { + this.album = metadata.tags.album + } + + if (metadata.tags.picture) { + const coverUpload = await uploadBinaryArrayToStorage(metadata.tags.picture) + + this.cover = coverUpload.url + } + } + + return this + } + + analyzeMetadata = async (file) => { + return new Promise((resolve, reject) => { + jsmediatags.read(file, { + onSuccess: (data) => { + return resolve(data) }, - }, + onError: (error) => { + return reject(error) + } + }) + }) + } +} + +class TracksManager extends React.Component { + state = { + list: [], + pendingUploads: [], + } + + componentDidMount() { + if (typeof this.props.list !== "undefined" && Array.isArray(this.props.list)) { + this.setState({ + list: this.props.list + }) + } + } + + componentDidUpdate = (prevProps, prevState) => { + if (prevState.list !== this.state.list || prevState.pendingUploads !== this.state.pendingUploads) { + if (typeof this.props.onChangeState === "function") { + this.props.onChangeState(this.state) + } + } + } + + findTrackByUid = (uid) => { + if (!uid) { + return false + } + + return this.state.list.find((item) => item.uid === uid) + } + + addTrackToList = (track) => { + if (!track) { + return false + } + + this.setState({ + list: [...this.state.list, track], }) } - return - { - (provided, snapshot) => { - return
-
- {props.index + 1} -
- - {track.title} - -
- } - onClick={onClickEditTrack} - /> - -
- -
-
-
- } + removeTrackByUid = (uid) => { + if (!uid) { + return false } -
-} - -const ReleaseTracks = (props) => { - const { release } = props - const [list, setList] = React.useState(release.list ?? []) - const [pendingTracksUpload, setPendingTracksUpload] = React.useState([]) + this.setState({ + list: this.state.list.filter((item) => item.uid !== uid), + }) + } - async function onTrackUploaderChange (change) { + addTrackUIDToPendingUploads = (uid) => { + if (!uid) { + return false + } + + if (!this.state.pendingUploads.includes(uid)) { + this.setState({ + pendingUploads: [...this.state.pendingUploads, uid], + }) + } + } + + removeTrackUIDFromPendingUploads = (uid) => { + if (!uid) { + return false + } + + this.setState({ + pendingUploads: this.state.pendingUploads.filter((item) => item !== uid), + }) + } + + handleUploaderStateChange = async (change) => { switch (change.file.status) { case "uploading": { - if (!pendingTracksUpload.includes(change.file.uid)) { - pendingTracksUpload.push(change.file.uid) - } + this.addTrackUIDToPendingUploads(change.file.uid) - setList((prev) => { - return [ - ...prev, - - ] + const trackManifest = new TrackManifest({ + uid: change.file.uid, + file: change.file, }) + await trackManifest.initialize() + + this.addTrackToList(trackManifest) + break } case "done": { // remove pending file - this.setState({ - pendingTracksUpload: this.state.pendingTracksUpload.filter((uid) => uid !== change.file.uid) - }) + this.removeTrackUIDFromPendingUploads(change.file.uid) - // update file url in the track info - const track = this.state.trackList.find((file) => file.uid === change.file.uid) + const trackIndex = this.state.list.findIndex((item) => item.uid === uid) - if (track) { - track.source = change.file.response.url - track.status = "done" + if (trackIndex === -1) { + console.error(`Track with uid [${uid}] not found!`) + break } - this.setState({ - trackList: this.state.trackList + // update track list + this.setState((state) => { + state.list[trackIndex].source = change.file.response.url + + return state }) break } case "error": { // remove pending file - this.handleTrackRemove(change.file.uid) + this.removeTrackUIDFromPendingUploads(change.file.uid) - // open a dialog to show the error and ask user to retry - antd.Modal.error({ - title: "Upload failed", - content: "An error occurred while uploading the file. You want to retry?", - cancelText: "No", - okText: "Retry", - onOk: () => { - this.handleUploadTrack(change) - }, - onCancel: () => { - this.handleTrackRemove(change.file.uid) - } - }) + // remove from tracklist + await this.removeTrackByUid(change.file.uid) } case "removed": { - this.handleTrackRemove(change.file.uid) + // stop upload & delete from pending list and tracklist + await this.removeTrackByUid(change.file.uid) } - default: { break } } } - async function handleUploadTrack (req) { + uploadToStorage = async (req) => { const response = await app.cores.remoteStorage.uploadFile(req.file, { - onProgress: this.handleFileProgress, - service: "premium-cdn" + onProgress: this.handleTrackFileUploadProgress, + service: "b2" }).catch((error) => { console.error(error) antd.message.error(error) @@ -172,38 +230,40 @@ const ReleaseTracks = (props) => { } } - async function onTrackDragEnd(result) { - console.log(result) + handleTrackFileUploadProgress = async (file, progress) => { + console.log(file, progress) + } + orderTrackList = (result) => { if (!result.destination) { return } - setList((prev) => { - const trackList = [...prev] + this.setState((prev) => { + const trackList = [...prev.list] const [removed] = trackList.splice(result.source.index, 1) trackList.splice(result.destination.index, 0, removed) - return trackList + return { + list: trackList + } }) } - return
-

Tracks

- -
+ render() { + return
{ - list.length === 0 ? + this.state.list.length === 0 ? : } @@ -212,7 +272,7 @@ const ReleaseTracks = (props) => { { className="music-studio-release-editor-tracks-list" > { - list.length === 0 && } { - list.map((track, index) => { + this.state.list.map((track, index) => { return {
+ } +} + +const ReleaseTracks = (props) => { + const { state, setState } = props + + return
+

Tracks

+ + { + setState({ + ...state, + ...managerState + }) + }} + />
} diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.less b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.less index 08b787f4..be2a2297 100644 --- a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.less +++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/index.less @@ -3,50 +3,4 @@ flex-direction: column; gap: 10px; -} - -.music-studio-release-editor-tracks-list-item { - position: relative; - - display: flex; - flex-direction: row; - - padding: 10px; - - gap: 10px; - - border-radius: 12px; - - background-color: var(--background-color-accent); - - .music-studio-release-editor-tracks-list-item-actions { - position: absolute; - - top: 0; - right: 0; - - display: flex; - - align-items: center; - justify-content: center; - - height: 100%; - - padding: 0 5px; - - svg { - margin: 0; - } - - .music-studio-release-editor-tracks-list-item-dragger { - display: flex; - - align-items: center; - justify-content: center; - - svg { - font-size: 1rem; - } - } - } } \ No newline at end of file diff --git a/packages/app/src/components/Poll/index.jsx b/packages/app/src/components/Poll/index.jsx index 9260ab5b..0da84d84 100644 --- a/packages/app/src/components/Poll/index.jsx +++ b/packages/app/src/components/Poll/index.jsx @@ -1,33 +1,102 @@ import React from "react" +import * as antd from "antd" +import classNames from "classnames" -import { createIconRender } from "@components/Icon" +import { createIconRender } from "@components/Icons" import "./index.less" const PollOption = (props) => { - return
-
- { - createIconRender(props.option.icon) - } + const { option, editMode, onRemove } = props - - {props.option.label} + return
+ { + editMode && + } + + { + !editMode && + {option.label} -
+ } + + { + editMode && + }
} const Poll = (props) => { + const { editMode, onClose } = props + + const [options, setOptions] = React.useState(props.options ?? []) + + async function addOption() { + setOptions((prev) => { + return [ + ...prev, + { + label: null + } + ] + }) + } + + async function removeOption(index) { + setOptions((prev) => { + return [ + ...prev.slice(0, index), + ...prev.slice(index + 1) + ] + }) + } + return
{ - props.options.map((option) => { + options.map((option, index) => { return { + removeOption(index) + }} /> }) } + + { + editMode &&
+ + Add Option + + + +
+ }
} diff --git a/packages/app/src/components/Poll/index.less b/packages/app/src/components/Poll/index.less index 6a15e297..1bfa50dc 100644 --- a/packages/app/src/components/Poll/index.less +++ b/packages/app/src/components/Poll/index.less @@ -2,25 +2,59 @@ display: flex; flex-direction: column; + width: 100%; + gap: 8px; + background-color: var(--background-color-primary); + border-radius: 12px; + + border: 2px solid var(--border-color); + + padding: 10px; + .poll-option { display: flex; flex-direction: row; - width: 100%; + align-items: center; - padding: 10px 20px; + gap: 10px; + + padding: 5px; + + width: 100%; transition: all 150ms ease-in-out; + border-radius: 6px; + background-color: var(--background-color-accent); - border-radius: 12px; cursor: pointer; - &:hover { - background-color: var(--background-color-accent-hover); + .ant-input { + background-color: transparent; + width: 100%; + + border: 0; + + color: var(--text-color); + + &::placeholder { + color: var(--text-color); + opacity: 0.5; + } } } + + .poll-edit-actions { + display: flex; + flex-direction: row; + + align-items: center; + justify-content: flex-end; + + gap: 10px; + } } \ No newline at end of file diff --git a/packages/app/src/components/PostCreator/index.jsx b/packages/app/src/components/PostCreator/index.jsx index 6cf46411..4dc273c4 100755 --- a/packages/app/src/components/PostCreator/index.jsx +++ b/packages/app/src/components/PostCreator/index.jsx @@ -4,10 +4,13 @@ import classnames from "classnames" import { DateTime } from "luxon" import lodash from "lodash" import humanSize from "@tsmx/human-readable" + import PostLink from "@components/PostLink" import { Icons } from "@components/Icons" +import Poll from "@components/Poll" import clipboardEventFileToFile from "@utils/clipboardEventFileToFile" + import PostModel from "@models/post" import "./index.less" @@ -26,6 +29,7 @@ export default class PostCreator extends React.Component { postMessage: "", postAttachments: [], + postPoll: null, fileList: [], postingPolicy: DEFAULT_POST_POLICY, @@ -451,6 +455,20 @@ export default class PostCreator extends React.Component { dialog.click() } + handleAddPoll = () => { + if (!this.state.postPoll) { + this.setState({ + postPoll: [] + }) + } + } + + handleDeletePoll = () => { + this.setState({ + postPoll: null + }) + } + componentDidMount = async () => { if (this.props.edit_post) { await this.setState({ @@ -589,6 +607,14 @@ export default class PostCreator extends React.Component {
+ { + this.state.postPoll && + } +
} + onClick={this.handleAddPoll} />
diff --git a/packages/app/src/contexts/MusicReleaseEditor/index.js b/packages/app/src/contexts/MusicReleaseEditor/index.js new file mode 100644 index 00000000..ed8df1d2 --- /dev/null +++ b/packages/app/src/contexts/MusicReleaseEditor/index.js @@ -0,0 +1,15 @@ +import React from "react" + +export const DefaultReleaseEditorState = { + cover: null, + title: "Untitled", + type: "single", + public: false, + + list: [], + pendingUploads: [], +} + +export const ReleaseEditorStateContext = React.createContext(DefaultReleaseEditorState) + +export default ReleaseEditorStateContext \ No newline at end of file diff --git a/packages/app/src/hooks/useHideToolsBar/index.js b/packages/app/src/hooks/useHideToolsBar/index.js new file mode 100644 index 00000000..b53ddc8b --- /dev/null +++ b/packages/app/src/hooks/useHideToolsBar/index.js @@ -0,0 +1,19 @@ +import React from "react" + +export default (to) => { + React.useEffect(() => { + if (typeof to !== "undefined") { + app.layout.tools_bar.toggleVisibility(to) + + return () => { + app.layout.tools_bar.toggleVisibility(!!to) + } + } + + app.layout.tools_bar.toggleVisibility(false) + + return () => { + app.layout.tools_bar.toggleVisibility(true) + } + }, []) +} \ No newline at end of file diff --git a/packages/app/src/hooks/useLayoutInterface/index.js b/packages/app/src/hooks/useLayoutInterface/index.js index d7948a96..cc9a6772 100755 --- a/packages/app/src/hooks/useLayoutInterface/index.js +++ b/packages/app/src/hooks/useLayoutInterface/index.js @@ -2,7 +2,7 @@ import React from "react" export default (namespace, ctx) => { React.useEffect(() => { - if (app.layout["namespace"] === "object") { + if (app.layout[namespace] === "object") { throw new Error(`Layout namespace [${namespace}] already exists`) } diff --git a/packages/app/src/layouts/components/bottomBar/index.jsx b/packages/app/src/layouts/components/@mobile/bottomBar/index.jsx similarity index 99% rename from packages/app/src/layouts/components/bottomBar/index.jsx rename to packages/app/src/layouts/components/@mobile/bottomBar/index.jsx index 8108dad1..ebbc7ac3 100755 --- a/packages/app/src/layouts/components/bottomBar/index.jsx +++ b/packages/app/src/layouts/components/@mobile/bottomBar/index.jsx @@ -8,7 +8,7 @@ import { Icons, createIconRender } from "@components/Icons" import { WithPlayerContext, Context } from "@contexts/WithPlayerContext" -import { QuickNavMenuItems, QuickNavMenu } from "@layouts/components/quickNav" +import { QuickNavMenuItems, QuickNavMenu } from "@layouts/components/@mobile/quickNav" import PlayerView from "@pages/@mobile-views/player" import CreatorView from "@pages/@mobile-views/creator" diff --git a/packages/app/src/layouts/components/bottomBar/index.less b/packages/app/src/layouts/components/@mobile/bottomBar/index.less similarity index 100% rename from packages/app/src/layouts/components/bottomBar/index.less rename to packages/app/src/layouts/components/@mobile/bottomBar/index.less diff --git a/packages/app/src/layouts/components/quickNav/index.jsx b/packages/app/src/layouts/components/@mobile/quickNav/index.jsx similarity index 100% rename from packages/app/src/layouts/components/quickNav/index.jsx rename to packages/app/src/layouts/components/@mobile/quickNav/index.jsx diff --git a/packages/app/src/layouts/components/topBar/index.jsx b/packages/app/src/layouts/components/@mobile/topBar/index.jsx similarity index 100% rename from packages/app/src/layouts/components/topBar/index.jsx rename to packages/app/src/layouts/components/@mobile/topBar/index.jsx diff --git a/packages/app/src/layouts/components/topBar/index.less b/packages/app/src/layouts/components/@mobile/topBar/index.less similarity index 100% rename from packages/app/src/layouts/components/topBar/index.less rename to packages/app/src/layouts/components/@mobile/topBar/index.less diff --git a/packages/app/src/layouts/components/draggableDrawer/index.jsx b/packages/app/src/layouts/components/draggableDrawer/index.jsx index d05a8c98..37609ede 100755 --- a/packages/app/src/layouts/components/draggableDrawer/index.jsx +++ b/packages/app/src/layouts/components/draggableDrawer/index.jsx @@ -9,6 +9,72 @@ import { css } from "@emotion/css" import "./index.less" +// TODO: Finish me pleassse +export class DraggableDrawerController extends Component { + constructor(props) { + super(props) + + this.interface = { + open: this.open, + close: this.close, + } + + this.state = { + drawers: [] + } + } + + componentDidMount() { + app.layout.draggable = this.interface + } + + open = (id, render, options = {}) => { + this.setState({ + drawers: [ + ...this.state.drawers, + { + id: id, + locked: options.defaultLocked ?? false, + render: + {React.createElement(render)} + , + } + ], + }) + } + + close = (id) => { + const drawerIndex = this.state.drawers.findIndex((drawer) => drawer.id === id) + + if (drawerIndex === -1) { + console.error("Drawer not found") + return false + } + + const drawer = this.state.drawers[drawerIndex] + + if (drawer.locked === true){ + return false + } + + const drawers = this.state.drawers + + drawers.splice(drawerIndex, 1) + + this.setState({ drawers: drawers }) + } + + render() { + return this.state.drawers.map((drawer) => drawer.render) + } +} + export default class DraggableDrawer extends Component { static propTypes = { open: PropTypes.bool.isRequired, diff --git a/packages/app/src/layouts/components/drawer/index.jsx b/packages/app/src/layouts/components/drawer/index.jsx index c351dbbb..84908e69 100755 --- a/packages/app/src/layouts/components/drawer/index.jsx +++ b/packages/app/src/layouts/components/drawer/index.jsx @@ -1,7 +1,93 @@ import React from "react" -import { EventBus } from "evite" -import { Drawer as AntdDrawer } from "antd" -import DraggableDrawer from "../draggableDrawer" +import classnames from "classnames" +import { Motion, spring } from "react-motion" + +import "./index.less" + +export class Drawer extends React.Component { + options = this.props.options ?? {} + + state = { + visible: false, + } + + toggleVisibility = (to) => { + to = to ?? !this.state.visible + + this.setState({ visible: to }) + } + + close = async () => { + if (typeof this.options.onClose === "function") { + this.options.onClose() + } + + this.toggleVisibility(false) + + this.props.controller.close(this.props.id, { + delay: 500 + }) + } + + handleDone = (...context) => { + if (typeof this.options.onDone === "function") { + this.options.onDone(this, ...context) + } + } + + handleFail = (...context) => { + if (typeof this.options.onFail === "function") { + this.options.onFail(this, ...context) + } + } + + componentDidMount = async () => { + if (typeof this.props.controller === "undefined") { + throw new Error(`Cannot mount an drawer without an controller`) + } + + if (typeof this.props.children === "undefined") { + throw new Error(`Empty component`) + } + + this.toggleVisibility(true) + } + + render() { + const componentProps = { + ...this.options.componentProps, + close: this.close, + handleDone: this.handleDone, + handleFail: this.handleFail, + } + + return + {({ x, opacity }) => { + return
+ + { + React.createElement(this.props.children, componentProps) + } +
+ }} +
+ } +} export default class DrawerController extends React.Component { constructor(props) { @@ -11,19 +97,99 @@ export default class DrawerController extends React.Component { addresses: {}, refs: {}, drawers: [], + + maskVisible: false, + maskRender: false, } - app.layout.drawer = { + this.interface = { open: this.open, close: this.close, closeAll: this.closeAll, drawersLength: () => this.state.drawers.length, + isMaskVisible: () => this.state.maskVisible, } } - sendEvent = (id, ...context) => { - const ref = this.state.refs[id]?.current - return ref.events.emit(...context) + componentDidMount = () => { + app.layout["drawer"] = this.interface + + this.listenEscape() + } + + componentWillUnmount = () => { + delete app.layout["drawer"] + + this.unlistenEscape() + } + + componentWillUpdate = (prevProps, prevState) => { + // is mask visible, hide sidebar with `app.layout.sidebar.toggleVisibility(false)` + if (prevState.maskVisible !== this.state.maskVisible) { + app.layout.sidebar.toggleVisibility(false) + } else if (prevState.maskRender !== this.state.maskRender) { + app.layout.sidebar.toggleVisibility(true) + } + } + + listenEscape = () => { + document.addEventListener("keydown", this.handleEscKeyPress) + } + + unlistenEscape = () => { + document.removeEventListener("keydown", this.handleEscKeyPress) + } + + handleEscKeyPress = (event) => { + if (this.state.drawers.length === 0) { + return false + } + + let isEscape = false + + if ("key" in event) { + isEscape = event.key === "Escape" || event.key === "Esc" + } else { + isEscape = event.keyCode === 27 + } + + if (isEscape) { + this.closeLastDrawer() + } + } + + getLastDrawer = () => { + return this.state.drawers[this.state.drawers.length - 1].ref.current + } + + closeLastDrawer = () => { + const lastDrawer = this.getLastDrawer() + + if (lastDrawer) { + lastDrawer.close() + } + } + + toggleMaskVisibility = async (to) => { + to = to ?? !this.state.maskVisible + + this.setState({ + maskVisible: to, + }) + + if (to === true) { + this.setState({ + maskRender: true + }) + } else { + await new Promise((resolve) => { + setTimeout(resolve, 500) + }) + + this.setState({ + maskRender: false + }) + } } open = (id, component, options) => { @@ -46,33 +212,37 @@ export default class DrawerController extends React.Component { addresses[id] = drawers.length - 1 refs[id] = instance.ref } else { - const ref = refs[id].current - const isLocked = ref.state.locked - - if (!isLocked) { - drawers[addresses[id]] = - refs[id] = instance.ref - } else { - console.warn("Cannot update an locked drawer.") - } + drawers[addresses[id]] = + refs[id] = instance.ref } - this.setState({ refs, addresses, drawers }) + this.setState({ + refs, + addresses, + drawers, + }) + + this.toggleMaskVisibility(true) } - close = (id) => { + close = async (id, { delay = 0 }) => { let { addresses, drawers, refs } = this.state const index = addresses[id] - const ref = this.state.refs[id]?.current if (typeof ref === "undefined") { return console.warn("This drawer not exists") } - if (ref.state.locked && ref.state.visible) { - return console.warn("This drawer is locked and cannot be closed") + if (drawers.length === 1) { + this.toggleMaskVisibility(false) + } + + if (delay > 0) { + await new Promise((resolve) => { + setTimeout(resolve, delay) + }) } if (typeof drawers[index] !== "undefined") { @@ -82,7 +252,11 @@ export default class DrawerController extends React.Component { delete addresses[id] delete refs[id] - this.setState({ addresses, drawers }) + this.setState({ + refs, + addresses, + drawers, + }) } closeAll = () => { @@ -92,129 +266,34 @@ export default class DrawerController extends React.Component { } render() { - return this.state.drawers - } -} + return <> + + {({ opacity }) => { + return
this.closeLastDrawer()} + style={{ + opacity, + display: this.state.maskRender ? "block" : "none", + }} + /> + }} + -export class Drawer extends React.Component { - options = this.props.options ?? {} - - events = new EventBus() - - state = { - type: this.options.type ?? "right", - visible: true, - locked: false, - } - - componentDidMount = async () => { - if (this.options.defaultLocked) { - this.setState({ locked: true }) - } - - if (typeof this.props.controller === "undefined") { - throw new Error(`Cannot mount an drawer without an controller`) - } - if (typeof this.props.children === "undefined") { - throw new Error(`Empty component`) - } - } - - toggleVisibility = (to) => { - this.setState({ visible: to ?? !this.state.visible }) - } - - lock = async () => { - return await this.setState({ locked: true }) - } - - unlock = async () => { - return await this.setState({ locked: false }) - } - - close = async ({ - unlock = false - } = {}) => { - return new Promise(async (resolve) => { - if (unlock) { - await this.setState({ locked: false }) - } - - if (this.state.locked && !unlock) { - return console.warn("Cannot close a locked drawer") - } - - this.toggleVisibility(false) - - this.events.emit("beforeClose") - - setTimeout(() => { - if (typeof this.options.onClose === "function") { - this.options.onClose() - } - - this.props.controller.close(this.props.id) - - resolve() - }, 500) - }) - } - - sendEvent = (...context) => { - return this.props.controller.sendEvent(this.props.id, ...context) - } - - handleDone = (...context) => { - if (typeof this.options.onDone === "function") { - this.options.onDone(this, ...context) - } - } - - handleFail = (...context) => { - if (typeof this.options.onFail === "function") { - this.options.onFail(this, ...context) - } - } - - render() { - const drawerProps = { - ...this.options.props, - ref: this.props.ref, - key: this.props.id, - onRequestClose: this.close, - onClose: this.close, - open: this.state.visible, - containerElementClass: "drawer", - modalElementClass: "body", - destroyOnClose: true, - } - const componentProps = { - ...this.options.componentProps, - locked: this.state.locked, - lock: this.lock, - unlock: this.unlock, - events: this.events, - close: this.close, - handleDone: this.handleDone, - handleFail: this.handleFail, - } - - switch (this.options.type) { - case "drawer": { - return +
- } - - default: { - return - { - React.createElement(this.props.children, componentProps) - } - - } - } + )} + > + {this.state.drawers} +
+ } } \ No newline at end of file diff --git a/packages/app/src/layouts/components/drawer/index.less b/packages/app/src/layouts/components/drawer/index.less new file mode 100644 index 00000000..4697dc5b --- /dev/null +++ b/packages/app/src/layouts/components/drawer/index.less @@ -0,0 +1,66 @@ +@import "@styles/vars.less"; + +.drawers-wrapper { + position: fixed; + + top: 0; + left: 0; + + z-index: 500; + + display: flex; + flex-direction: row; + + padding: @sidebar_padding; + + margin-left: calc(@sidebar_padding * 2); + + height: 100dvh; + height: 100vh; + + // &.hidden { + // display: none; + // } +} + +.drawers-mask { + position: fixed; + + top: 0; + left: 0; + + z-index: 500; + + width: 100vw; + height: 100vh; + + background-color: rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4px); + + +} + +.drawer { + position: relative; + + z-index: 550; + + top: 0; + left: 0; + bottom: 0; + + width: fit-content; + min-width: 320px; + height: 100%; + + padding: 20px; + + background-color: var(--background-color-accent); + + border-radius: @sidebar_borderRadius; + box-shadow: @card-shadow; + border: 1px solid var(--sidebar-background-color); + + overflow-x: hidden; + overflow-y: overlay; +} \ No newline at end of file diff --git a/packages/app/src/layouts/components/modals/index.jsx b/packages/app/src/layouts/components/modals/index.jsx index 150387ab..a2c42270 100755 --- a/packages/app/src/layouts/components/modals/index.jsx +++ b/packages/app/src/layouts/components/modals/index.jsx @@ -1,118 +1,10 @@ import React from "react" -import { Modal as AntdModal } from "antd" -import classnames from "classnames" - -import { Icons } from "@components/Icons" +import Modal from "./modal" import useLayoutInterface from "@hooks/useLayoutInterface" -import "./index.less" - -class Modal extends React.Component { - state = { - visible: false, - } - - contentRef = React.createRef() - - escTimeout = null - - componentDidMount() { - setTimeout(() => { - this.setState({ - visible: true, - }) - }, 10) - - document.addEventListener("keydown", this.handleEsc, false) - } - - componentWillUnmount() { - document.removeEventListener("keydown", this.handleEsc, false) - } - - close = () => { - this.setState({ - visible: false, - }) - - setTimeout(() => { - if (typeof this.props.onClose === "function") { - this.props.onClose() - } - }, 250) - } - - handleEsc = (e) => { - if (e.key === "Escape") { - if (this.escTimeout !== null) { - clearTimeout(this.escTimeout) - return this.close() - } - - this.escTimeout = setTimeout(() => { - this.escTimeout = null - }, 250) - } - } - - handleClickOutside = (e) => { - if (this.props.confirmOnOutsideClick) { - return AntdModal.confirm({ - title: this.props.confirmOnClickTitle ?? "Are you sure?", - content: this.props.confirmOnClickContent ?? "Are you sure you want to close this window?", - onOk: () => { - this.close() - } - }) - } - - return this.close() - } - - render() { - return
-
-
- { - this.props.includeCloseButton &&
- -
- } - - { - React.cloneElement(this.props.children, { - close: this.close - }) - } -
-
- } -} - export default () => { - const modalRef = React.useRef() - - function openModal( + function open( id, render, { @@ -127,7 +19,6 @@ export default () => { } = {} ) { app.cores.window_mng.render(id, { app.cores.window_mng.close(id) }} @@ -143,13 +34,13 @@ export default () => { ) } - function closeModal() { - modalRef.current.close() + function close(id) { + app.cores.window_mng.close(id) } useLayoutInterface("modal", { - open: openModal, - close: closeModal, + open: open, + close: close, }) return null diff --git a/packages/app/src/layouts/components/modals/index.less b/packages/app/src/layouts/components/modals/index.less index 45275f76..e69de29b 100755 --- a/packages/app/src/layouts/components/modals/index.less +++ b/packages/app/src/layouts/components/modals/index.less @@ -1,117 +0,0 @@ -@import "@styles/vars.less"; - -.app_modal_wrapper { - box-sizing: border-box; - - position: fixed; - top: 0; - left: 0; - - display: flex; - flex-direction: column; - - align-items: center; - justify-content: center; - - width: 100%; - height: 100%; - - transition: all 150ms ease-in-out; - - #mask_trigger { - position: fixed; - - top: 0; - left: 0; - - - width: 100vw; - height: 100vh; - } - - &.framed { - .app_modal_content { - display: flex; - flex-direction: column; - - padding: 30px; - - background-color: var(--background-color-accent); - - border-radius: 12px; - } - } - - &.active { - background-color: rgba(var(--bg_color_6), 0.1); - - backdrop-filter: blur(@modal_background_blur); - -webkit-backdrop-filter: blur(@modal_background_blur); - - .app_modal_content { - opacity: 1; - transform: translateY(0); - } - } - - .app_modal_content { - position: relative; - - box-sizing: border-box; - - display: flex; - flex-direction: column; - - align-items: center; - justify-content: center; - - opacity: 0; - transform: translateY(100px); - - height: fit-content; - width: 40vw; - - max-width: 600px; - - transition: all 150ms ease-in-out; - - .app_modal_close { - position: sticky; - - align-self: flex-end; - - top: 0; - right: 0; - - font-size: 1.5rem; - - cursor: pointer; - - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - - padding: 5px; - - border-radius: 12px; - - svg { - margin: 0; - color: var(--text-color); - } - } - - // fixments - .postCreator { - box-shadow: @card-shadow; - -webkit-box-shadow: @card-shadow; - -moz-box-shadow: @card-shadow; - } - - .searcher { - box-sizing: border-box; - - width: 48vw; - height: 80vh; - } - } -} \ No newline at end of file diff --git a/packages/app/src/layouts/components/modals/modal/index.jsx b/packages/app/src/layouts/components/modals/modal/index.jsx new file mode 100644 index 00000000..ff39d2cb --- /dev/null +++ b/packages/app/src/layouts/components/modals/modal/index.jsx @@ -0,0 +1,110 @@ +import React from "react" +import { Modal as AntdModal } from "antd" +import classnames from "classnames" + +import { Icons } from "@components/Icons" + +import "./index.less" + +class Modal extends React.Component { + state = { + visible: false, + } + + contentRef = React.createRef() + + escTimeout = null + + componentDidMount() { + setTimeout(() => { + this.setState({ + visible: true, + }) + }, 10) + + document.addEventListener("keydown", this.handleEsc, false) + } + + componentWillUnmount() { + document.removeEventListener("keydown", this.handleEsc, false) + } + + close = () => { + this.setState({ + visible: false, + }) + + setTimeout(() => { + if (typeof this.props.onClose === "function") { + this.props.onClose() + } + }, 250) + } + + handleEsc = (e) => { + if (e.key === "Escape") { + if (this.escTimeout !== null) { + clearTimeout(this.escTimeout) + return this.close() + } + + this.escTimeout = setTimeout(() => { + this.escTimeout = null + }, 250) + } + } + + handleClickOutside = (e) => { + if (this.props.confirmOnOutsideClick) { + return AntdModal.confirm({ + title: this.props.confirmOnClickTitle ?? "Are you sure?", + content: this.props.confirmOnClickContent ?? "Are you sure you want to close this window?", + onOk: () => { + this.close() + } + }) + } + + return this.close() + } + + render() { + return
+
+
+ { + this.props.includeCloseButton &&
+ +
+ } + + { + React.cloneElement(this.props.children, { + close: this.close + }) + } +
+
+ } +} + +export default Modal \ No newline at end of file diff --git a/packages/app/src/layouts/components/modals/modal/index.less b/packages/app/src/layouts/components/modals/modal/index.less new file mode 100644 index 00000000..45275f76 --- /dev/null +++ b/packages/app/src/layouts/components/modals/modal/index.less @@ -0,0 +1,117 @@ +@import "@styles/vars.less"; + +.app_modal_wrapper { + box-sizing: border-box; + + position: fixed; + top: 0; + left: 0; + + display: flex; + flex-direction: column; + + align-items: center; + justify-content: center; + + width: 100%; + height: 100%; + + transition: all 150ms ease-in-out; + + #mask_trigger { + position: fixed; + + top: 0; + left: 0; + + + width: 100vw; + height: 100vh; + } + + &.framed { + .app_modal_content { + display: flex; + flex-direction: column; + + padding: 30px; + + background-color: var(--background-color-accent); + + border-radius: 12px; + } + } + + &.active { + background-color: rgba(var(--bg_color_6), 0.1); + + backdrop-filter: blur(@modal_background_blur); + -webkit-backdrop-filter: blur(@modal_background_blur); + + .app_modal_content { + opacity: 1; + transform: translateY(0); + } + } + + .app_modal_content { + position: relative; + + box-sizing: border-box; + + display: flex; + flex-direction: column; + + align-items: center; + justify-content: center; + + opacity: 0; + transform: translateY(100px); + + height: fit-content; + width: 40vw; + + max-width: 600px; + + transition: all 150ms ease-in-out; + + .app_modal_close { + position: sticky; + + align-self: flex-end; + + top: 0; + right: 0; + + font-size: 1.5rem; + + cursor: pointer; + + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + + padding: 5px; + + border-radius: 12px; + + svg { + margin: 0; + color: var(--text-color); + } + } + + // fixments + .postCreator { + box-shadow: @card-shadow; + -webkit-box-shadow: @card-shadow; + -moz-box-shadow: @card-shadow; + } + + .searcher { + box-sizing: border-box; + + width: 48vw; + height: 80vh; + } + } +} \ No newline at end of file diff --git a/packages/app/src/layouts/components/sidebar/index.jsx b/packages/app/src/layouts/components/sidebar/index.jsx index 0bcdc791..d677105c 100755 --- a/packages/app/src/layouts/components/sidebar/index.jsx +++ b/packages/app/src/layouts/components/sidebar/index.jsx @@ -4,19 +4,69 @@ import classnames from "classnames" import { Translation } from "react-i18next" import { Motion, spring } from "react-motion" import { Menu, Avatar, Dropdown } from "antd" +import Drawer from "@layouts/components/drawer" import { Icons, createIconRender } from "@components/Icons" +import { GiLockedChest } from "react-icons/gi" import sidebarItems from "@config/sidebar" import "./index.less" +const builtInApps = [ + { + key: "hb", + label: "Hotel", + icon: "MdGames", + location: "/apps/hb", + }, + { + key: "pay", + label: "Pay", + icon: "MdPayment", + location: "/apps/pay", + }, + { + key: "loots", + label: "Loots", + icon: , + location: "/apps/loots", + } +] + +const AppDrawer = (props) => { + return
+

Apps

+ + { + builtInApps.map((item) => { + return
{ + if (item.location) { + app.location.push(item.location) + } + + props.close() + }} + > +

{item.icon && createIconRender(item.icon)} {item.label}

+
+ }) + } +
+} + const onClickHandlers = { + apps: () => { + app.layout.drawer.open("apps", AppDrawer) + }, addons: () => { - window.app.location.push("/addons") + window.app.location.push("/addons") }, studio: () => { - window.app.location.push("/studio") + window.app.location.push("/studio") }, settings: () => { window.app.navigation.goToSettings() @@ -28,12 +78,12 @@ const onClickHandlers = { window.app.controls.openSearcher() }, messages: () => { - window.app.controls.openMessages() + window.app.controls.openMessages() }, create: () => { window.app.controls.openCreator() }, - account: () => { + profile: () => { window.app.navigation.goToAccount() }, login: () => { @@ -84,6 +134,13 @@ const BottomMenuDefaultItems = [ , icon: , }, + { + key: "apps", + label: + {(t) => t("Apps")} + , + icon: , + }, { key: "settings", label: @@ -258,15 +315,7 @@ export default class Sidebar extends React.Component { } events = { - "sidedrawers.visible": (has) => { - this.setState({ - lockAutocollapse: has - }) - if (!has && this.state.expanded) { - this.interface.toggleCollapse(false) - } - } } componentDidMount = async () => { @@ -316,7 +365,7 @@ export default class Sidebar extends React.Component { return app.location.push(`/${item.path ?? e.key}`, 150) } - onMouseEnter = () => { + onMouseEnter = (event) => { if (!this.state.visible) return if (window.app.cores.settings.is("sidebar.collapsable", false)) { @@ -327,10 +376,15 @@ export default class Sidebar extends React.Component { return } + // do nothing if is mask visible + if (app.layout.drawer.isMaskVisible()) { + return false + } + this.interface.toggleCollapse(true) } - handleMouseLeave = () => { + handleMouseLeave = (event) => { if (!this.state.visible) return if (window.app.cores.settings.is("sidebar.collapsable", false)) return @@ -431,10 +485,12 @@ export default class Sidebar extends React.Component { } ref={this.sidebarRef} > -
- + app.navigation.goMain()} + />
@@ -463,6 +519,8 @@ export default class Sidebar extends React.Component { />
+ +
}}
diff --git a/packages/app/src/layouts/components/sidebar/index.less b/packages/app/src/layouts/components/sidebar/index.less index a26c31e7..86485bb8 100755 --- a/packages/app/src/layouts/components/sidebar/index.less +++ b/packages/app/src/layouts/components/sidebar/index.less @@ -32,6 +32,9 @@ .app_sidebar { position: relative; + + z-index: 1000; + display: flex; flex-direction: column; @@ -80,6 +83,11 @@ padding: 0; } + &.disabled { + pointer-events: none; + opacity: 0.5; + } + .app_sidebar_header { display: flex; flex-direction: column; @@ -174,9 +182,9 @@ &.user_avatar { .ant-menu-title-content { width: 100%; - + display: inline-flex; - + align-items: flex-start; justify-content: center; @@ -192,8 +200,6 @@ } } - - padding: 0 !important; } } diff --git a/packages/app/src/layouts/components/sidedrawer/index.jsx b/packages/app/src/layouts/components/sidedrawer/index.jsx deleted file mode 100755 index 0aaffa05..00000000 --- a/packages/app/src/layouts/components/sidedrawer/index.jsx +++ /dev/null @@ -1,262 +0,0 @@ -import React from "react" -import classnames from "classnames" -import { Motion, spring } from "react-motion" - -import "./index.less" - -export class Sidedrawer extends React.Component { - state = { - visible: false, - } - - toggleVisibility = (to) => { - this.setState({ visible: to ?? !this.state.visible }) - } - - render() { - return - {({ x, opacity }) => { - return
- { - React.createElement(this.props.children, { - ...this.props.props, - close: this.props.close, - }) - } -
- }} -
- } -} - -export default class SidedrawerController extends React.Component { - constructor(props) { - super(props) - - this.interface = app.layout.sidedrawer = { - open: this.open, - close: this.close, - closeAll: this.closeAll, - hasDrawers: this.state.drawers.length > 0, - toggleGlobalVisibility: () => { - this.setState({ - globalVisible: !this.state.globalVisible, - }) - } - } - } - - state = { - globalVisible: true, - drawers: [], - lockedIds: [], - } - - componentDidMount = () => { - this.listenEscape() - } - - componentWillUnmount = () => { - this.unlistenEscape() - } - - drawerIsLocked = (id) => { - return this.state.lockedIds.includes(id) - } - - lockDrawerId = (id) => { - this.setState({ - lockedIds: [...this.state.lockedIds, id], - }) - } - - unlockDrawer = (id) => { - this.setState({ - lockedIds: this.state.lockedIds.filter(lockedId => lockedId !== id), - }) - } - - open = async (id, component, options = {}) => { - if (typeof id !== "string") { - options = component - component = id - id = component.key ?? component.id ?? Math.random().toString(36).substr(2, 9) - } - - const drawers = this.state.drawers - - // check if id is already in use - // but only if its allowed to be used multiple times - const existentDrawer = drawers.find((drawer) => drawer.props.id === id) - - if (existentDrawer) { - if (!existentDrawer.props.allowMultiples) { - console.warn(`Sidedrawer with id "${id}" already exists.`) - return false - } - - // fix id appending the corresponding array index at the end of the id - // ex ["A", "B", "C"] => ["A", "B", "C", "A-1"] - // to prevent id collisions - - let index = 0 - let newId = id - - while (drawers.find(drawer => drawer.props.id === newId)) { - index++ - newId = id + "-" + index - } - - id = newId - } - - const drawerProps = { - id: id, - allowMultiples: options.allowMultiples ?? false, - escClosable: options.escClosable ?? true, - first: drawers.length === 0, - style: { - zIndex: 100 - drawers.length, - }, - ref: React.createRef(), - close: this.close, - lock: () => this.lockDrawerId(id), - unlock: () => this.unlockDrawer(id), - } - - drawers.push(React.createElement(Sidedrawer, drawerProps, component)) - - if (options.lock) { - this.lockDrawerId(id) - } - - await this.setState({ - drawers, - }) - - setTimeout(() => { - this.toggleDrawerVisibility(id, true) - }, 10) - - window.app.eventBus.emit("sidedrawer.open") - - if (this.state.drawers.length > 0) { - app.eventBus.emit("sidedrawers.visible", true) - } - } - - toggleDrawerVisibility = (id, to) => { - // find drawer - const drawer = this.state.drawers.find(drawer => drawer.props.id === id) - - if (!drawer) { - console.warn(`Sidedrawer with id "${id}" does not exist.`) - return - } - - if (!drawer.ref.current) { - console.warn(`Sidedrawer with id "${id}" has not valid ref.`) - return - } - - return drawer.ref.current.toggleVisibility(to) - } - - close = (id) => { - // if an id is provided filter by key - // else close the last opened drawer - let drawers = this.state.drawers - let drawerId = id ?? drawers[drawers.length - 1].props.id - - // check if id is locked - if (this.drawerIsLocked(id)) { - console.warn(`Sidedrawer with id "${id}" is locked.`) - return false - } - - // check if id exists - const drawer = drawers.find(drawer => drawer.props.id === drawerId) - - if (!drawer) { - console.warn(`Sidedrawer with id "${id}" does not exist.`) - return false - } - - // emit event - window.app.eventBus.emit("sidedrawer.close") - - // toggleVisibility off - this.toggleDrawerVisibility(drawerId, false) - - // await drawer transition - setTimeout(() => { - // remove drawer - drawers = drawers.filter(drawer => drawer.props.id !== drawerId) - - this.setState({ drawers }) - - if (this.state.drawers.length === 0) { - app.eventBus.emit("sidedrawers.visible", false) - } - }, 500) - } - - listenEscape = () => { - document.addEventListener("keydown", this.handleEscKeyPress) - } - - unlistenEscape = () => { - document.removeEventListener("keydown", this.handleEscKeyPress) - } - - handleEscKeyPress = (event) => { - // avoid handle keypress when is nothing to render - if (this.state.drawers.length === 0) { - return false - } - - let isEscape = false - - if ("key" in event) { - isEscape = event.key === "Escape" || event.key === "Esc" - } else { - isEscape = event.keyCode === 27 - } - - if (isEscape) { - // close the last opened drawer - this.close() - } - } - - render() { - return
- {this.state.drawers} -
- } -} \ No newline at end of file diff --git a/packages/app/src/layouts/components/sidedrawer/index.less b/packages/app/src/layouts/components/sidedrawer/index.less deleted file mode 100755 index 5f927380..00000000 --- a/packages/app/src/layouts/components/sidedrawer/index.less +++ /dev/null @@ -1,48 +0,0 @@ -@import "@styles/vars.less"; - -.sidedrawers-wrapper { - position: relative; - top: 0; - right: 0; - - display: flex; - flex-direction: row; - - padding: @sidebar_padding; - - height: 100dvh; - height: 100vh; - - .sidedrawer { - position: absolute; - - z-index: 100; - - top: 0; - left: 0; - bottom: 0; - - margin-top: @sidebar_padding; - height: calc(100% - @sidebar_padding * 2); - - width: auto; - - min-width: 400px; - max-width: 15vw; - - background-color: var(--sidedrawer-background-color); - border-radius: @sidebar_borderRadius; - - padding: 20px; - - overflow-x: hidden; - overflow-y: overlay; - - box-shadow: @card-shadow; - } - - &.hidden{ - width: 0; - padding: 0; - } -} \ No newline at end of file diff --git a/packages/app/src/layouts/default/index.jsx b/packages/app/src/layouts/default/index.jsx index fb864856..5801d860 100755 --- a/packages/app/src/layouts/default/index.jsx +++ b/packages/app/src/layouts/default/index.jsx @@ -3,26 +3,24 @@ import classnames from "classnames" import { Layout } from "antd" import Sidebar from "@layouts/components/sidebar" -import Drawer from "@layouts/components/drawer" -import Sidedrawer from "@layouts/components/sidedrawer" -import BottomBar from "@layouts/components/bottomBar" -import TopBar from "@layouts/components/topBar" import ToolsBar from "@layouts/components/toolsBar" import Header from "@layouts/components/header" -import InitializeModalsController from "@layouts/components/modals" +import Modals from "@layouts/components/modals" + +// mobile components +import { DraggableDrawerController } from "@layouts/components/draggableDrawer" +import BottomBar from "@layouts/components/@mobile/bottomBar" +import TopBar from "@layouts/components/@mobile/topBar" import BackgroundDecorator from "@components/BackgroundDecorator" const DesktopLayout = (props) => { - InitializeModalsController() - return <> + - - { const MobileLayout = (props) => { return + { - } diff --git a/packages/app/src/layouts/minimal/index.jsx b/packages/app/src/layouts/minimal/index.jsx index 1abc646b..f7b7a9e3 100755 --- a/packages/app/src/layouts/minimal/index.jsx +++ b/packages/app/src/layouts/minimal/index.jsx @@ -1,14 +1,15 @@ import React from "react" import * as antd from "antd" import classnames from "classnames" +import { DraggableDrawerController } from "@layouts/components/draggableDrawer" import Drawer from "@layouts/components/drawer" -import Sidedrawer from "@layouts/components/sidedrawer" export default (props) => { return - + +
{React.cloneElement(props.children, props)}
diff --git a/packages/app/src/pages/auth/index.mobile.jsx b/packages/app/src/pages/auth/index.mobile.jsx index cfc54b00..9ce2211e 100755 --- a/packages/app/src/pages/auth/index.mobile.jsx +++ b/packages/app/src/pages/auth/index.mobile.jsx @@ -1,31 +1,16 @@ import React from "react" +import useRandomFeaturedWallpaperUrl from "@hooks/useRandomFeaturedWallpaperUrl" + import "./index.mobile.less" export default (props) => { - const [wallpaperData, setWallpaperData] = React.useState(null) - - const setRandomWallpaper = async () => { - const { data: featuredWallpapers } = await app.cores.api.customRequest({ - method: "GET", - url: "/featured_wallpapers" - }).catch((err) => { - console.error(err) - return [] - }) - - // get random wallpaper from array - const randomWallpaper = featuredWallpapers[Math.floor(Math.random() * featuredWallpapers.length)] - - setWallpaperData(randomWallpaper) - } + const randomWallpaperURL = useRandomFeaturedWallpaperUrl() React.useEffect(() => { if (app.userData) { app.navigation.goMain() } else { - setRandomWallpaper() - app.controls.openLoginForm({ defaultLocked: true, }) @@ -35,7 +20,7 @@ export default (props) => { return
diff --git a/packages/app/src/pages/lyrics/components/controller/index.jsx b/packages/app/src/pages/lyrics/components/controller/index.jsx index 85e21477..5018317b 100644 --- a/packages/app/src/pages/lyrics/components/controller/index.jsx +++ b/packages/app/src/pages/lyrics/components/controller/index.jsx @@ -64,6 +64,7 @@ const PlayerController = React.forwardRef((props, ref) => { setDraggingTime(false) app.cores.player.seek(seekTime) + syncPlayback() } @@ -91,7 +92,6 @@ const PlayerController = React.forwardRef((props, ref) => { React.useEffect(() => { setTitleIsOverflown(isOverflown(titleRef.current)) setTrackDuration(app.cores.player.duration()) - console.log(context.track_manifest) }, [context.track_manifest]) React.useEffect(() => { @@ -104,7 +104,7 @@ const PlayerController = React.forwardRef((props, ref) => { className={classnames( "lyrics-player-controller-wrapper", { - ["hidden"]: hide, + ["hidden"]: props.lyrics?.video_source && hide, } )} onMouseEnter={onMouseEnter} diff --git a/packages/app/src/pages/lyrics/index.jsx b/packages/app/src/pages/lyrics/index.jsx index 96cfa840..54c9ef69 100644 --- a/packages/app/src/pages/lyrics/index.jsx +++ b/packages/app/src/pages/lyrics/index.jsx @@ -25,10 +25,14 @@ const EnchancedLyrics = (props) => { async function loadLyrics(track_id) { const result = await MusicService.getTrackLyrics(track_id, { preferTranslation: translationEnabled, + }).catch((err) => { + return null }) if (result) { setLyrics(result) + } else { + setLyrics(false) } } @@ -49,7 +53,7 @@ const EnchancedLyrics = (props) => { //* Handle when context change track_manifest React.useEffect(() => { setLyrics(null) - + if (context.track_manifest) { loadLyrics(context.track_manifest._id) } @@ -72,6 +76,20 @@ const EnchancedLyrics = (props) => { } )} > + { + !lyrics?.video_source &&
+
+ +
+
+ } + { isRemoteTyping, } = useChat(to_user_id) - console.log(R_User) async function submitMessage(e) { diff --git a/packages/app/src/pages/messages/index.jsx b/packages/app/src/pages/messages/index.jsx index 1a75f763..38d30642 100644 --- a/packages/app/src/pages/messages/index.jsx +++ b/packages/app/src/pages/messages/index.jsx @@ -1,7 +1,6 @@ import React from "react" import * as antd from "antd" - import ChatsService from "@models/chats" import TimeAgo from "@components/TimeAgo" diff --git a/packages/app/src/pages/nfc/[tag_id].jsx b/packages/app/src/pages/nfc/[tag_id].jsx index f81ed6c5..47630277 100755 --- a/packages/app/src/pages/nfc/[tag_id].jsx +++ b/packages/app/src/pages/nfc/[tag_id].jsx @@ -27,6 +27,13 @@ export default (props) => { case "profile": { return app.navigation.goToAccount(result.behavior.value) } + case "random_list": { + const values = result.behavior.value.split(";") + + const index = Math.floor(Math.random() * values.length) + + return window.location.href = values[index] + } } } diff --git a/packages/app/src/settings/about/index.jsx b/packages/app/src/settings/about/index.jsx index 4e7966ef..348273bb 100755 --- a/packages/app/src/settings/about/index.jsx +++ b/packages/app/src/settings/about/index.jsx @@ -1,7 +1,7 @@ import React from "react" import * as antd from "antd" -import { version as linebridgeVersion } from "linebridge/package.json" +// import { version as linebridgeVersion } from "linebridge/package.json" import { Icons } from "@components/Icons" import LatencyIndicator from "@components/PerformanceIndicators/latency" @@ -157,6 +157,20 @@ export default {
+
+
+
+ +
+ +

Platform

+
+ +
+ {Capacitor.platform} +
+
+
@@ -177,21 +191,7 @@ export default {
-

Linebridge Engine

-
- -
- {linebridgeVersion ?? globalThis._linebrige_version ?? "Unknown"} -
-
- -
-
-
- -
- -

Evite Framework

+

Engine

@@ -205,7 +205,7 @@ export default {
-

Comty.JS

+

Comty.js

@@ -213,20 +213,6 @@ export default {
-
-
-
- -
- -

Platform

-
- -
- {Capacitor.platform} -
-
- { capInfo &&
diff --git a/packages/app/src/settings/api/index.jsx b/packages/app/src/settings/api/index.jsx new file mode 100644 index 00000000..6ef90cd0 --- /dev/null +++ b/packages/app/src/settings/api/index.jsx @@ -0,0 +1,81 @@ +import React from "react" +import * as antd from "antd" + +import "./index.less" + +const useGetMainOrigin = () => { + const [mainOrigin, setMainOrigin] = React.useState(null) + + React.useEffect(() => { + const instance = app.cores.api.client() + + if (instance) { + setMainOrigin(instance.mainOrigin) + } + + return () => { + setMainOrigin(null) + } + }, []) + + return mainOrigin +} + +export default { + id: "api", + icon: "TbApi", + label: "API", + group: "advanced", + render: () => { + const mainOrigin = useGetMainOrigin() + const [keys, setKeys] = React.useState([]) + + return
+
+

+ Main Origin +

+

+ {mainOrigin} +

+
+ +
+
+
+

Your Keys

+

Manage your API keys

+
+ + + Create new + +
+ +
+ { + keys.map((key) => { + return null + }) + } + { + keys.length === 0 && + } +
+
+ + +
+ } +} \ No newline at end of file diff --git a/packages/app/src/settings/api/index.less b/packages/app/src/settings/api/index.less new file mode 100644 index 00000000..77874408 --- /dev/null +++ b/packages/app/src/settings/api/index.less @@ -0,0 +1,32 @@ +.developer-settings { + display: flex; + flex-direction: column; + + gap: 20px; +} + +.api_keys { + display: flex; + flex-direction: column; + + background-color: var(--background-color-accent); + + padding: 10px; + + border-radius: 12px; + + .api_keys_header { + display: flex; + flex-direction: row; + + align-items: center; + justify-content: space-between; + } +} + +.links { + display: flex; + flex-direction: column; + + gap: 5px; +} \ No newline at end of file diff --git a/packages/app/src/settings/apparence/index.jsx b/packages/app/src/settings/apparence/index.jsx index 8dfdb5f9..d830679e 100755 --- a/packages/app/src/settings/apparence/index.jsx +++ b/packages/app/src/settings/apparence/index.jsx @@ -14,7 +14,6 @@ export default { { id: "style:variant_mode", group: "aspect", - component: "Switch", icon: "Moon", title: "Theme", description: "Change the theme of the application.", @@ -52,7 +51,7 @@ export default { max: 1.2, step: 0.01, tooltip: { - formatter: (value) => `${value}x` + formatter: (value) => `x${value}` } }, defaultValue: () => { @@ -143,8 +142,6 @@ export default { defaultValue: () => { const value = app.cores.style.getVar("backgroundImage") - console.log(value) - return value ? value.replace(/url\(|\)/g, "") : "" }, onUpdate: (value) => { diff --git a/packages/app/src/settings/changelogs/index.jsx b/packages/app/src/settings/changelogs/index.jsx index 84300ee0..143ab605 100755 --- a/packages/app/src/settings/changelogs/index.jsx +++ b/packages/app/src/settings/changelogs/index.jsx @@ -14,7 +14,7 @@ import "./index.less" const FetchChangelogs = async () => { const response = await app.cores.api.customRequest({ method: "GET", - url: `/release-notes`, + url: `/repo/releases-notes`, }) return response.data diff --git a/packages/app/src/settings/components/changePassword/index.jsx b/packages/app/src/settings/components/changePassword/index.jsx index 8ffa03c2..852a2d3e 100755 --- a/packages/app/src/settings/components/changePassword/index.jsx +++ b/packages/app/src/settings/components/changePassword/index.jsx @@ -125,11 +125,7 @@ const ChangePasswordComponent = (props) => { export default (props) => { const onClick = () => { - if (app.isMobile) { - return app.layout.drawer.open("passwordChange", ChangePasswordComponent) - } - - return app.layout.sidedrawer.open("passwordChange", ChangePasswordComponent) + return app.layout.drawer.open("passwordChange", ChangePasswordComponent) } return diff --git a/packages/app/src/settings/components/themeVariantSelector/index.less b/packages/app/src/settings/components/themeVariantSelector/index.less index 8f8cc413..4e33e528 100644 --- a/packages/app/src/settings/components/themeVariantSelector/index.less +++ b/packages/app/src/settings/components/themeVariantSelector/index.less @@ -7,6 +7,8 @@ border: 1px var(--border-color) solid; border-radius: 12px; + width: fit-content; + overflow: hidden; .__setting_theme_variant_selector-variant { diff --git a/packages/app/src/settings/general/index.jsx b/packages/app/src/settings/general/index.jsx index a1922d80..a3e139c1 100755 --- a/packages/app/src/settings/general/index.jsx +++ b/packages/app/src/settings/general/index.jsx @@ -60,7 +60,7 @@ export default { group: "ui.sounds", component: "Switch", icon: "MdVolumeUp", - title: "UI effects", + title: "Effects", description: "Enable the UI effects.", mobile: false, }, @@ -70,8 +70,8 @@ export default { group: "ui.sounds", component: "Slider", icon: "MdVolumeUp", - title: "UI volume", - description: "Set the volume of the app sounds.", + title: "Volume", + description: "Set the volume of the app UI sounds.", props: { tipFormatter: (value) => { return `${value}%` @@ -108,7 +108,7 @@ export default { group: "notifications", component: "Slider", icon: "MdVolumeUp", - title: "Sound Volume", + title: "Volume", description: "Set the volume of the sound when a notification is received.", props: { tipFormatter: (value) => { diff --git a/packages/app/src/settings/tap_share/index.jsx b/packages/app/src/settings/tap_share/index.jsx index 7f1a2c60..0dcdf9e8 100755 --- a/packages/app/src/settings/tap_share/index.jsx +++ b/packages/app/src/settings/tap_share/index.jsx @@ -152,7 +152,7 @@ const TagItem = (props) => { } danger - disabled + onClick={props.onDelete} />
@@ -192,6 +192,25 @@ class OwnTags extends React.Component { }) } + handleTagDelete = (tag) => { + antd.Modal.confirm({ + title: "Are you sure you want to delete this tag?", + content: `This action cannot be undone.`, + onOk: async () => { + NFCModel.deleteTag(tag._id) + .then(() => { + app.message.success("Tag deleted") + this.loadData() + }) + .catch((err) => { + console.error(err) + app.message.error(err.message) + return false + }) + } + }) + } + handleTagRead = async (error, tag) => { if (error) { console.error(error) @@ -252,6 +271,9 @@ class OwnTags extends React.Component { tag }) }} + onDelete={() => { + this.handleTagDelete(tag) + }} /> }) } diff --git a/packages/app/src/settings/tap_share/steps/data_editor/index.jsx b/packages/app/src/settings/tap_share/steps/data_editor/index.jsx index c3bcd946..1d90098e 100755 --- a/packages/app/src/settings/tap_share/steps/data_editor/index.jsx +++ b/packages/app/src/settings/tap_share/steps/data_editor/index.jsx @@ -137,9 +137,9 @@ export default (props) => { - Post + Random list diff --git a/packages/app/src/styles/index.less b/packages/app/src/styles/index.less index af35fb8f..bba8c028 100755 --- a/packages/app/src/styles/index.less +++ b/packages/app/src/styles/index.less @@ -331,4 +331,17 @@ svg { } } } +} + +.card { + display: flex; + flex-direction: column; + + background-color: var(--background-color-accent); + + padding: 10px; + + border-radius: 12px; + + gap: 10px; } \ No newline at end of file diff --git a/packages/app/vite.config.js b/packages/app/vite.config.js index 050a4670..fac87fc1 100755 --- a/packages/app/vite.config.js +++ b/packages/app/vite.config.js @@ -1,23 +1,10 @@ import path from "path" +import aliases from "./aliases" import { defineConfig } from "vite" import react from "@vitejs/plugin-react" -const aliases = { - "@": path.join(__dirname, "src"), - "@config": path.join(__dirname, "config"), - "@cores": path.join(__dirname, "src/cores"), - "@pages": path.join(__dirname, "src/pages"), - "@styles": path.join(__dirname, "src/styles"), - "@components": path.join(__dirname, "src/components"), - "@contexts": path.join(__dirname, "src/contexts"), - "@utils": path.join(__dirname, "src/utils"), - "@layouts": path.join(__dirname, "src/layouts"), - "@hooks": path.join(__dirname, "src/hooks"), - "@classes": path.join(__dirname, "src/classes"), - "@models": path.join(__dirname, "../../", "comty.js/src/models"), - "comty.js": path.join(__dirname, "../../", "comty.js", "src"), -} +const oneYearInSeconds = 60 * 60 * 24 * 365 export default defineConfig({ plugins: [ @@ -35,7 +22,10 @@ export default defineConfig({ https: { key: path.join(__dirname, "ssl", "privkey.pem"), cert: path.join(__dirname, "ssl", "cert.pem"), - } + }, + headers: { + "Strict-Transport-Security": `max-age=${oneYearInSeconds}` + }, }, css: { preprocessorOptions: { diff --git a/packages/server/.dockerignore b/packages/server/.dockerignore new file mode 100644 index 00000000..571157e3 --- /dev/null +++ b/packages/server/.dockerignore @@ -0,0 +1,8 @@ +./node_modules +./build +./dist +./ssl + +# secrets +./api.production.env +./.env \ No newline at end of file diff --git a/packages/server/.swcrc b/packages/server/.swcrc new file mode 100644 index 00000000..4d32447a --- /dev/null +++ b/packages/server/.swcrc @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/swcrc", + "exclude":[ + "node_modules/minio/**", + "node_modules/@octokit/**" + ], + "module": { + "type": "commonjs", + // These are defaults. + "strict": false, + "strictMode": true, + "lazy": false, + "noInterop": false + } +} \ No newline at end of file diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile index 9305ff34..bfd8f6a8 100755 --- a/packages/server/Dockerfile +++ b/packages/server/Dockerfile @@ -1,27 +1,36 @@ -FROM node:16-bullseye-slim +FROM node:18-bookworm-slim +EXPOSE 9000 +# Install dependencies RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list - RUN apt update -RUN apt install --no-install-recommends curl ffmpeg python yarn build-essential -y +RUN apt install -y --no-install-recommends build-essential +RUN apt install -y --no-install-recommends curl +RUN apt install -y --no-install-recommends ffmpeg +RUN apt install -y --no-install-recommends yarn +RUN apt install -y --no-install-recommends git +RUN apt install -y --no-install-recommends ssh +RUN apt install -y --no-install-recommends ca-certificates -RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app +# Create workdir +RUN mkdir -p /comty-server +WORKDIR /comty-server -WORKDIR /home/node/app +# Copy Files +COPY package.json ./ +COPY . . + +# Fix permissions +RUN chmod -R 777 /comty-server +RUN chown -R node:node /comty-server + +# Set user to node USER node -EXPOSE 3010 - -COPY package.json ./ -COPY --chown=node:node . . - -RUN chmod -R 777 /home/node/app - -RUN export NODE_ENV=production - -RUN yarn global add cross-env -RUN yarn install --production -RUN yarn build +# Install modules & rebuild for host +RUN npm install RUN npm rebuild @tensorflow/tfjs-node --build-from-source -CMD ["yarn", "run", "run:prod"] \ No newline at end of file +# Start server +RUN export NODE_ENV=production +CMD ["npm", "run", "start:prod"] \ No newline at end of file diff --git a/packages/server/docker-compose.yml b/packages/server/docker-compose.yml new file mode 100644 index 00000000..3f4c064e --- /dev/null +++ b/packages/server/docker-compose.yml @@ -0,0 +1,11 @@ +services: + api: + container_name: comty-api + build: . + restart: unless-stopped + ports: + - "9000:9000" + env_file: + - ./api.production.env + volumes: + - ./ssl:/comty-server/ssl \ No newline at end of file diff --git a/packages/server/gateway/index.js b/packages/server/gateway/index.js index dfcba1f8..2d5337fa 100644 --- a/packages/server/gateway/index.js +++ b/packages/server/gateway/index.js @@ -5,7 +5,7 @@ import Spinnies from "spinnies" import { Observable } from "@gullerya/object-observer" import { dots as DefaultSpinner } from "spinnies/spinners.json" import EventEmitter from "@foxify/events" -import IPCRouter from "linebridge/src/server/classes/IPCRouter" +import IPCRouter from "linebridge/dist/server/classes/IPCRouter" import chokidar from "chokidar" import { onExit } from "signal-exit" import chalk from "chalk" diff --git a/packages/server/gateway/proxy.js b/packages/server/gateway/proxy.js index 7dcaf4ab..76b1afee 100644 --- a/packages/server/gateway/proxy.js +++ b/packages/server/gateway/proxy.js @@ -1,5 +1,5 @@ import httpProxy from "http-proxy" -import defaults from "linebridge/src/server/defaults" +import defaults from "linebridge/dist/server/defaults" import pkg from "../package.json" diff --git a/packages/server/package.json b/packages/server/package.json index 83eb7f33..b110698b 100755 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,36 +1,54 @@ { - "name": "@comty/server", - "version": "0.70.0", - "license": "MIT", - "workspaces": [ - "services/*" - ], - "private": true, - "scripts": { - "build": "hermes build", - "dev": "cross-env NODE_ENV=development hermes-node ./index.js", - "run:prod": "cross-env NODE_ENV=production node ./dist/index.js" - }, - "dependencies": { - "@gullerya/object-observer": "^6.1.3", - "@infisical/sdk": "^2.1.8", - "@ragestudio/hermes": "^0.1.1", - "chalk": "4.1.2", - "dotenv": "^16.4.4", - "http-proxy": "^1.18.1", - "linebridge": "^0.18.1", - "module-alias": "^2.2.3", - "nodejs-snowflake": "^2.0.1", - "signal-exit": "^4.1.0", - "spinnies": "^0.5.1", - "tree-kill": "^1.2.2" - }, - "devDependencies": { - "chai": "^5.1.0", - "cross-env": "^7.0.3", - "mocha": "^10.3.0" - }, - "resolutions": { - "string-width": "4.2.3" - } + "name": "@comty/server", + "version": "0.70.0", + "license": "MIT", + "workspaces": [ + "services/*" + ], + "private": true, + "scripts": { + "start:prod": "cross-env NODE_ENV=production hermes-node ./index.js", + "dev": "cross-env NODE_ENV=development hermes-node ./index.js", + "build:bin": "cd build && pkg ./index.js" + }, + "dependencies": { + "@gullerya/object-observer": "^6.1.3", + "@infisical/sdk": "^2.1.8", + "@ragestudio/hermes": "^0.1.1", + "axios": "^1.7.4", + "bcrypt": "^5.1.1", + "chalk": "4.1.2", + "comty.js": "^0.60.3", + "dotenv": "^16.4.4", + "http-proxy": "^1.18.1", + "ioredis": "^5.4.1", + "jsonwebtoken": "^9.0.2", + "linebridge": "^0.20.3", + "minio": "^8.0.1", + "module-alias": "^2.2.3", + "mongoose": "^8.5.3", + "nodejs-snowflake": "^2.0.1", + "qs": "^6.13.0", + "signal-exit": "^4.1.0", + "spinnies": "^0.5.1", + "tree-kill": "^1.2.2" + }, + "devDependencies": { + "@swc-node/register": "^1.10.9", + "@swc/cli": "^0.3.12", + "@swc/core": "^1.4.11", + "chai": "^5.1.0", + "cross-env": "^7.0.3", + "mocha": "^10.3.0", + "pkg": "^5.8.1" + }, + "resolutions": { + "string-width": "4.2.3" + }, + "pkg": { + "targets": [ + "node18-linux-arm64" + ], + "outputPath": "dist" + } } diff --git a/packages/server/services/auth/auth.service.js b/packages/server/services/auth/auth.service.js index 7994910f..c673830a 100644 --- a/packages/server/services/auth/auth.service.js +++ b/packages/server/services/auth/auth.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" import SharedMiddlewares from "@shared-middlewares" diff --git a/packages/server/services/auth/package.json b/packages/server/services/auth/package.json index 64192631..932e2eb2 100644 --- a/packages/server/services/auth/package.json +++ b/packages/server/services/auth/package.json @@ -1,10 +1,4 @@ { "name": "auth", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "proxy":{ - "namespace": "/auth", - "port": 3020 - } + "version": "1.0.0" } diff --git a/packages/server/services/auth/routes/auth/post.js b/packages/server/services/auth/routes/auth/post.js index be750a8e..0d5a123e 100644 --- a/packages/server/services/auth/routes/auth/post.js +++ b/packages/server/services/auth/routes/auth/post.js @@ -42,17 +42,18 @@ export default async (req, res) => { if (mfaSession) { if (!req.body.mfa_code) { - await mfaSession.delete() + await MFASession.deleteMany({ user_id: user._id }) } else { if (mfaSession.expires_at < new Date().getTime()) { - await mfaSession.delete() + await MFASession.deleteMany({ user_id: user._id }) throw new OperationError(401, "MFA code expired, login again...") } if (mfaSession.code == req.body.mfa_code) { codeVerified = true - await mfaSession.delete() + + await MFASession.deleteMany({ user_id: user._id }) } else { throw new OperationError(401, "Invalid MFA code, try again...") } diff --git a/packages/server/services/auth/routes/server-keys/my/get.js b/packages/server/services/auth/routes/server-keys/my/get.js new file mode 100644 index 00000000..153ce6db --- /dev/null +++ b/packages/server/services/auth/routes/server-keys/my/get.js @@ -0,0 +1,7 @@ + +export default { + middlewares: ["withAuthentication"], + fn: async (req, res) => { + + } +} \ No newline at end of file diff --git a/packages/server/services/chats/chats.service.js b/packages/server/services/chats/chats.service.js index 00762f2f..45fab2e8 100755 --- a/packages/server/services/chats/chats.service.js +++ b/packages/server/services/chats/chats.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" import RedisClient from "@shared-classes/RedisClient" diff --git a/packages/server/services/chats/package.json b/packages/server/services/chats/package.json index 15ef1e24..4e136e1b 100755 --- a/packages/server/services/chats/package.json +++ b/packages/server/services/chats/package.json @@ -1,25 +1,4 @@ { "name": "chats", - "version": "0.60.2", - "license": "MIT", - "dependencies": { - "@foxify/events": "^2.1.0", - "axios": "^1.4.0", - "bcrypt": "5.0.1", - "comty.js": "^0.58.2", - "connect-mongo": "^4.6.0", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.2", - "jsonwebtoken": "8.5.1", - "linebridge": "0.15.12", - "luxon": "^3.0.4", - "minio": "^7.0.32", - "moment": "2.29.4", - "moment-timezone": "0.5.37", - "mongoose": "^6.9.0", - "morgan": "^1.10.0", - "redis": "^4.6.6", - "socket.io": "^4.5.4" - } + "version": "0.60.2" } \ No newline at end of file diff --git a/packages/server/services/chats/routes/chats/[chat_id]/history/get.js b/packages/server/services/chats/routes/chats/[chat_id]/history/get.js index 0bc02207..ba8d2dda 100644 --- a/packages/server/services/chats/routes/chats/[chat_id]/history/get.js +++ b/packages/server/services/chats/routes/chats/[chat_id]/history/get.js @@ -64,7 +64,7 @@ export default { history = await Promise.all(history) return { - total: await ChatMessage.count(query), + total: await ChatMessage.countDocuments(query), offset: offset, limit: limit, order: order, diff --git a/packages/server/services/chats/routes_ws/chat/send/message.js b/packages/server/services/chats/routes_ws/chat/send/message.js index d7394dab..3dffd912 100644 --- a/packages/server/services/chats/routes_ws/chat/send/message.js +++ b/packages/server/services/chats/routes_ws/chat/send/message.js @@ -28,8 +28,6 @@ export default async (socket, payload, engine) => { const targetSocket = await engine.find.socketByUserId(payload.to_user_id) - console.log(targetSocket) - if (targetSocket) { await targetSocket.emit("chat:receive:message", wsMessageObj) } diff --git a/packages/server/services/ems/ems.service.js b/packages/server/services/ems/ems.service.js index 4516ad92..b0b47e2b 100644 --- a/packages/server/services/ems/ems.service.js +++ b/packages/server/services/ems/ems.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import nodemailer from "nodemailer" import DbManager from "@shared-classes/DbManager" diff --git a/packages/server/services/ems/package.json b/packages/server/services/ems/package.json index c1beaa73..0f28a259 100644 --- a/packages/server/services/ems/package.json +++ b/packages/server/services/ems/package.json @@ -2,12 +2,6 @@ "name": "ems", "description": "External Messaging Service (SMS, EMAIL, PUSH)", "version": "0.1.0", - "main": "index.js", - "license": "MIT", - "proxy": { - "path": "/ems", - "port": 3007 - }, "dependencies": { "handlebars": "^4.7.8", "nodemailer": "^6.9.11", diff --git a/packages/server/services/files/file.service.js b/packages/server/services/files/file.service.js index e3e77d89..88f39622 100755 --- a/packages/server/services/files/file.service.js +++ b/packages/server/services/files/file.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import B2 from "backblaze-b2" diff --git a/packages/server/services/files/package.json b/packages/server/services/files/package.json index 21fad390..02adaaba 100755 --- a/packages/server/services/files/package.json +++ b/packages/server/services/files/package.json @@ -1,31 +1,15 @@ { "name": "files", "version": "0.60.2", - "license": "MIT", "dependencies": { - "@foxify/events": "^2.1.0", - "axios": "^1.4.0", "backblaze-b2": "^1.7.0", - "bcrypt": "^5.1.0", "busboy": "^1.6.0", - "comty.js": "^0.58.2", - "connect-mongo": "^4.6.0", "content-range": "^2.0.2", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.2", "fluent-ffmpeg": "^2.1.2", - "jsonwebtoken": "^9.0.0", - "linebridge": "0.15.12", - "luxon": "^3.0.4", "merge-files": "^0.1.2", "mime-types": "^2.1.35", "sharp": "0.32.6", "minio": "^7.0.32", - "moment": "^2.29.4", - "moment-timezone": "^0.5.40", - "mongoose": "^6.9.0", - "morgan": "^1.10.0", "normalize-url": "^8.0.0", "p-map": "4.0.0", "p-queue": "^7.3.4", diff --git a/packages/server/services/files/routes/upload/chunk/post.js b/packages/server/services/files/routes/upload/chunk/post.js index 14469497..ff18b53d 100644 --- a/packages/server/services/files/routes/upload/chunk/post.js +++ b/packages/server/services/files/routes/upload/chunk/post.js @@ -5,6 +5,8 @@ import ChunkFileUpload from "@shared-classes/ChunkFileUpload" import RemoteUpload from "@services/remoteUpload" +const availableProviders = ["b2", "standard"] + export default { useContext: ["cache", "limits"], middlewares: [ @@ -34,6 +36,11 @@ export default { limits.useProvider = req.headers["provider-type"] ?? "b2" } + // check if provider is valid + if (!availableProviders.includes(limits.useProvider)) { + throw new OperationError(400, "Invalid provider") + } + let build = await ChunkFileUpload(req, { tmpDir: tmpPath, ...limits, diff --git a/packages/server/services/main/controllers/AdminController/endpoints/accounts_statistics.js b/packages/server/services/main/controllers/AdminController/endpoints/accounts_statistics.js deleted file mode 100755 index 3e910a66..00000000 --- a/packages/server/services/main/controllers/AdminController/endpoints/accounts_statistics.js +++ /dev/null @@ -1,58 +0,0 @@ -import { User, Session, Post } from "@db_models" - -export default { - method: "GET", - route: "/accounts_statistics", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - // get number of users registered, - const users = await User.count() - - // calculate the last 5 days logins from diferent users - let last5D_logins = await Session.find({ - date: { - $gte: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000), - $lte: new Date(), - } - }) - - const last5D_logins_counts = [] - - // filter from different users - last5D_logins.forEach((session) => { - if (!last5D_logins_counts.includes(session.user_id)) { - last5D_logins_counts.push(session.user_id) - } - }) - - // calculate active users within 1 week (using postings) - const active_1w_posts_users = await Post.count({ - user_id: { - $in: last5D_logins_counts - }, - created_at: { - $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), - $lte: new Date(), - } - }) - - // calculate total posts - const total_posts = await Post.count() - - // calculate total post (1week) - const total_posts_1w = await Post.count({ - created_at: { - $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), - $lte: new Date(), - } - }) - - return res.json({ - accounts_registered: users, - last5D_logins: last5D_logins_counts.length, - active_1w_posts_users: active_1w_posts_users, - total_posts: total_posts, - total_posts_1w: total_posts_1w, - }) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/AdminController/endpoints/delete_featured_wallpaper.js b/packages/server/services/main/controllers/AdminController/endpoints/delete_featured_wallpaper.js deleted file mode 100755 index 15dfea3b..00000000 --- a/packages/server/services/main/controllers/AdminController/endpoints/delete_featured_wallpaper.js +++ /dev/null @@ -1,26 +0,0 @@ -import { FeaturedWallpaper } from "@db_models" - -export default { - method: "DELETE", - route: "/featured_wallpaper/:id", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const id = req.params.id - - const wallpaper = await FeaturedWallpaper.findById(id) - - if (!wallpaper) { - return res.status(404).json({ - error: "Cannot find wallpaper" - }) - } - - await FeaturedWallpaper.deleteOne({ - _id: id - }) - - return res.json({ - done: true - }) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/AdminController/endpoints/featured_wallpaper.js b/packages/server/services/main/controllers/AdminController/endpoints/featured_wallpaper.js deleted file mode 100755 index 37658319..00000000 --- a/packages/server/services/main/controllers/AdminController/endpoints/featured_wallpaper.js +++ /dev/null @@ -1,39 +0,0 @@ -import { FeaturedWallpaper } from "@db_models" -import momentTimezone from "moment-timezone" - -export default { - method: "PUT", - route: "/featured_wallpaper", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const data = req.body.wallpaper - - if (!data) { - return res.status(400).json({ - error: "Invalid data" - }) - } - - // try to find if data._id exists, else create a new one - let wallpaper = null - - if (data._id) { - wallpaper = await FeaturedWallpaper.findOne({ - _id: data._id - }) - } else { - wallpaper = new FeaturedWallpaper() - } - - const current_timezone = momentTimezone.tz.guess() - - wallpaper.active = data.active ?? wallpaper.active ?? true - wallpaper.date = data.date ?? momentTimezone.tz(Date.now(), current_timezone).format() - wallpaper.url = data.url ?? wallpaper.url - wallpaper.author = data.author ?? wallpaper.author - - await wallpaper.save() - - return res.json(wallpaper) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/AdminController/endpoints/resetPassword.js b/packages/server/services/main/controllers/AdminController/endpoints/resetPassword.js deleted file mode 100755 index 7973baab..00000000 --- a/packages/server/services/main/controllers/AdminController/endpoints/resetPassword.js +++ /dev/null @@ -1,36 +0,0 @@ -import { User } from "@db_models" - -import bcrypt from "bcrypt" - -export default { - method: "POST", - route: "/update_password/:user_id", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const { password } = req.body - - if (!password) { - return res.status(400).json({ message: "Missing password" }) - } - - const { user_id } = req.params - - const user = await User.findById(user_id).select("+password") - - if (!user) { - return res.status(404).json({ message: "User not found" }) - } - - // hash the password - const hash = bcrypt.hashSync(password, parseInt(process.env.BCRYPT_ROUNDS ?? 3)) - - user.password = hash - - await user.save() - - return res.status(200).json({ - status: "ok", - message: "Password updated successfully", - }) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/AdminController/endpoints/update_user_data.js b/packages/server/services/main/controllers/AdminController/endpoints/update_user_data.js deleted file mode 100755 index b08f5986..00000000 --- a/packages/server/services/main/controllers/AdminController/endpoints/update_user_data.js +++ /dev/null @@ -1,35 +0,0 @@ -import { User } from "@db_models" - -export default { - method: "POST", - route: "/update_data/:user_id", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const targetUserId = req.params.user_id - - const user = await User.findById(targetUserId).catch((err) => { - return false - }) - - if (!user) { - return res.status(404).json({ error: "No user founded" }) - } - - const updateKeys = Object.keys(req.body.update) - - updateKeys.forEach((key) => { - user[key] = req.body.update[key] - }) - - await user.save() - - global.engine.ws.io.of("/").emit(`user.update`, { - ...user.toObject(), - }) - global.engine.ws.io.of("/").emit(`user.update.${targetUserId}`, { - ...user.toObject(), - }) - - return res.json(user.toObject()) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/AdminController/index.js b/packages/server/services/main/controllers/AdminController/index.js deleted file mode 100755 index 8ad46ce0..00000000 --- a/packages/server/services/main/controllers/AdminController/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from "linebridge/dist/server" -import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir" - -export default class AdminController extends Controller { - static refName = "AdminController" - static useRoute = "/admin" - - httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints") -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/AutoUpdate/index.js b/packages/server/services/main/controllers/AutoUpdate/index.js deleted file mode 100755 index bc062989..00000000 --- a/packages/server/services/main/controllers/AutoUpdate/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import { Controller } from "linebridge/dist/server" - -import { Octokit } from "@octokit/rest" - -const octokit = new Octokit({}) - -const endpoints = { - post: { - "/mobile": { - fn: async (req, res) => { - if (!process.env.GITHUB_REPO) { - return res.status(400).json({ - error: "GITHUB_REPO env variable not set" - }) - } - - const lastRelease = await octokit.repos.getLatestRelease({ - owner: process.env.GITHUB_REPO.split("/")[0], - repo: process.env.GITHUB_REPO.split("/")[1] - }) - - const bundle = lastRelease.data.assets.find((asset) => asset.name === "mobile_dist.zip") - const version = lastRelease.data.tag_name - - if (!bundle) { - return res.status(400).json({ - error: "mobile asset not available", - version: version, - }) - } - - return res.json({ - url: bundle.browser_download_url, - version: version, - }) - } - } - } -} - -export default class AutoUpdate extends Controller { - static refName = "AutoUpdate" - static useRoute = "/auto-update" - - httpEndpoints = endpoints -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/endpoints/deleteBadge.js b/packages/server/services/main/controllers/BadgesController/endpoints/deleteBadge.js deleted file mode 100755 index ca061bfe..00000000 --- a/packages/server/services/main/controllers/BadgesController/endpoints/deleteBadge.js +++ /dev/null @@ -1,21 +0,0 @@ -import { Badge } from "@db_models" - -export default { - method: "DELETE", - route: "/badge/:badge_id", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const badge = await Badge.findById(req.params.badge_id).catch((err) => { - res.status(500).json({ error: err }) - return false - }) - - if (!badge) { - return res.status(404).json({ error: "No badge founded" }) - } - - badge.remove() - - return res.json(badge) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/endpoints/getBadges.js b/packages/server/services/main/controllers/BadgesController/endpoints/getBadges.js deleted file mode 100755 index e5cdeda2..00000000 --- a/packages/server/services/main/controllers/BadgesController/endpoints/getBadges.js +++ /dev/null @@ -1,29 +0,0 @@ -import { Schematized } from "@lib" -import { Badge } from "@db_models" - -export default { - method: "GET", - route: "/", - fn: Schematized({ - select: ["_id", "name", "label"], - }, async (req, res) => { - let badges = [] - - if (req.selection._id) { - badges = await Badge.find({ - _id: { $in: req.selection._id }, - }) - - badges = badges.map(badge => badge.toObject()) - } else { - badges = await Badge.find(req.selection).catch((err) => { - res.status(500).json({ error: err }) - return false - }) - } - - if (badges) { - return res.json(badges) - } - }) -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/endpoints/getUserBadges.js b/packages/server/services/main/controllers/BadgesController/endpoints/getUserBadges.js deleted file mode 100755 index 7b777572..00000000 --- a/packages/server/services/main/controllers/BadgesController/endpoints/getUserBadges.js +++ /dev/null @@ -1,21 +0,0 @@ -import { User, Badge } from "@db_models" - -export default { - method: "GET", - route: "/user/:user_id", - fn: async (req, res) => { - const user = await User.findOne({ - _id: req.params.user_id, - }) - - if (!user) { - return res.status(404).json({ error: "User not found" }) - } - - const badges = await Badge.find({ - name: { $in: user.badges }, - }) - - return res.json(badges) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/endpoints/giveToUser.js b/packages/server/services/main/controllers/BadgesController/endpoints/giveToUser.js deleted file mode 100755 index 0db01a2b..00000000 --- a/packages/server/services/main/controllers/BadgesController/endpoints/giveToUser.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Badge, User } from "@db_models" -import { Schematized } from "@lib" - -export default { - method: "POST", - route: "/badge/:badge_id/giveToUser", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: Schematized({ - required: ["user_id"], - select: ["user_id"], - }, async (req, res) => { - const badge = await Badge.findById(req.params.badge_id).catch((err) => { - res.status(500).json({ error: err }) - return false - }) - - if (!badge) { - return res.status(404).json({ error: "No badge founded" }) - } - - const user = await User.findById(req.selection.user_id).catch((err) => { - res.status(500).json({ error: err }) - return false - }) - - if (!user) { - return res.status(404).json({ error: "No user founded" }) - } - - // check if user already have this badge - if (user.badges.includes(badge._id)) { - return res.status(409).json({ error: "User already have this badge" }) - } - - user.badges.push(badge._id.toString()) - - user.save() - - return res.json(user) - }) -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/endpoints/putBadge.js b/packages/server/services/main/controllers/BadgesController/endpoints/putBadge.js deleted file mode 100755 index 62e38896..00000000 --- a/packages/server/services/main/controllers/BadgesController/endpoints/putBadge.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Badge } from "@db_models" -import { Schematized } from "@lib" - -export default { - method: "PUT", - route: "/", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: Schematized({ - select: ["badge_id", "name", "label", "description", "icon", "color"], - }, async (req, res) => { - let badge = await Badge.findById(req.selection.badge_id).catch((err) => null) - - if (!badge) { - badge = new Badge() - } - - badge.name = req.selection.name || badge.name - badge.label = req.selection.label || badge.label - badge.description = req.selection.description || badge.description - badge.icon = req.selection.icon || badge.icon - badge.color = req.selection.color || badge.color - - badge.save() - - return res.json(badge) - }) -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/endpoints/removeToUser.js b/packages/server/services/main/controllers/BadgesController/endpoints/removeToUser.js deleted file mode 100755 index fd9ce0cc..00000000 --- a/packages/server/services/main/controllers/BadgesController/endpoints/removeToUser.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Schematized } from "@lib" -import { Badge, User } from "@db_models" - -export default { - method: "DELETE", - route: "/badge/:badge_id/removeFromUser", - middlewares: ["withAuthentication", "onlyAdmin"], - fn: Schematized({ - required: ["user_id"], - select: ["user_id"], - }, async (req, res) => { - const badge = await Badge.findById(req.params.badge_id).catch((err) => { - res.status(500).json({ error: err }) - return false - }) - - if (!badge) { - return res.status(404).json({ error: "No badge founded" }) - } - - const user = await User.findById(req.selection.user_id).catch((err) => { - res.status(500).json({ error: err }) - return false - }) - - if (!user) { - return res.status(404).json({ error: "No user founded" }) - } - - // check if user already have this badge - if (!user.badges.includes(badge._id)) { - return res.status(409).json({ error: "User don't have this badge" }) - } - - user.badges = user.badges.filter(b => b !== badge._id.toString()) - - user.save() - - return res.json(user) - }) -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/BadgesController/index.js b/packages/server/services/main/controllers/BadgesController/index.js deleted file mode 100755 index 3ce58f83..00000000 --- a/packages/server/services/main/controllers/BadgesController/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from "linebridge/dist/server" -import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir" - -export default class BadgesController extends Controller { - static refName = "BadgesController" - static useRoute = "/badge" - - httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints") -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/FeaturedEventsController/index.js b/packages/server/services/main/controllers/FeaturedEventsController/index.js deleted file mode 100755 index a1d91325..00000000 --- a/packages/server/services/main/controllers/FeaturedEventsController/index.js +++ /dev/null @@ -1,63 +0,0 @@ -import { Controller } from "linebridge/dist/server" - -import { FeaturedEvent } from "@db_models" -import createFeaturedEvent from "./services/createFeaturedEvent" - -// TODO: Migrate to new linebridge 0.15 endpoint classes instead of this - -export default class FeaturedEventsController extends Controller { - httpEndpoints = { - get: { - "/featured_event/:id": async (req, res) => { - const { id } = req.params - - const featuredEvent = await FeaturedEvent.findById(id) - - return res.json(featuredEvent) - }, - "/featured_events": async (req, res) => { - let query = { - expired: false - } - - if (req.query.includeExpired) { - delete query.expired - } - - const featuredEvents = await FeaturedEvent.find(query) - - return res.json(featuredEvents) - } - }, - post: { - "/featured_event": { - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const result = await createFeaturedEvent(req.body).catch((err) => { - res.status(500).json({ - error: err.message - }) - - return null - }) - - if (result) { - return res.json(result) - } - } - } - }, - delete: { - "/featured_event/:id": { - middlewares: ["withAuthentication", "onlyAdmin"], - fn: async (req, res) => { - const { id } = req.params - - const featuredEvent = await FeaturedEvent.findByIdAndDelete(id) - - return res.json(featuredEvent) - } - } - }, - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/FeaturedEventsController/services/createFeaturedEvent.js b/packages/server/services/main/controllers/FeaturedEventsController/services/createFeaturedEvent.js deleted file mode 100755 index 97c25de7..00000000 --- a/packages/server/services/main/controllers/FeaturedEventsController/services/createFeaturedEvent.js +++ /dev/null @@ -1,27 +0,0 @@ -import { FeaturedEvent } from "@db_models" - -export default async (payload) => { - const { - name, - category, - description, - dates, - location, - announcement, - customHeader, - } = payload - - const featuredEvent = new FeaturedEvent({ - name, - category, - description, - dates, - location, - announcement, - customHeader, - }) - - await featuredEvent.save() - - return featuredEvent -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/NFCController/endpoints/getExecution.js b/packages/server/services/main/controllers/NFCController/endpoints/getExecution.js deleted file mode 100755 index 6d63dc4a..00000000 --- a/packages/server/services/main/controllers/NFCController/endpoints/getExecution.js +++ /dev/null @@ -1,11 +0,0 @@ -export default { - method: "GET", - route: "/execution/:user_id", - fn: async (req, res) => { - let execution = { - - } - - return res.json(execution) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/NFCController/index.js b/packages/server/services/main/controllers/NFCController/index.js deleted file mode 100755 index 2691c570..00000000 --- a/packages/server/services/main/controllers/NFCController/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from "linebridge/dist/server" -import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir" - -export default class NFCController extends Controller { - static refName = "NFCController" - static useRoute = "/nfc" - - httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints") -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/endpoints/featuredWallpapers.js b/packages/server/services/main/controllers/PublicController/endpoints/featuredWallpapers.js deleted file mode 100755 index 2b92f6f9..00000000 --- a/packages/server/services/main/controllers/PublicController/endpoints/featuredWallpapers.js +++ /dev/null @@ -1,28 +0,0 @@ -import { FeaturedWallpaper } from "@db_models" - -export default { - method: "GET", - route: "/featured_wallpapers", - fn: async (req, res) => { - const { all } = req.query - - const query = { - active: true - } - - if (all) { - delete query.active - } - - const featuredWallpapers = await FeaturedWallpaper.find(query) - .sort({ date: -1 }) - .limit(all ? undefined : 10) - .catch(err => { - return res.status(500).json({ - error: err.message - }).end() - }) - - return res.json(featuredWallpapers) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/endpoints/getReleasesNotes.js b/packages/server/services/main/controllers/PublicController/endpoints/getReleasesNotes.js deleted file mode 100755 index f3b61341..00000000 --- a/packages/server/services/main/controllers/PublicController/endpoints/getReleasesNotes.js +++ /dev/null @@ -1,49 +0,0 @@ -import { Octokit } from "@octokit/rest" -import axios from "axios" - -const octokit = new Octokit({}) - -export default { - method: "GET", - route: "/release-notes", - fn: async (req, res) => { - if (!process.env.GITHUB_REPO) { - return res.status(400).json({ - error: "GITHUB_REPO env variable not set" - }) - } - - const releasesNotes = [] - - // fetch the 3 latest releases - const releases = await octokit.repos.listReleases({ - owner: process.env.GITHUB_REPO.split("/")[0], - repo: process.env.GITHUB_REPO.split("/")[1], - per_page: 3 - }) - - for await (const release of releases.data) { - let changelogData = release.body - - const bundle = release.assets.find((asset) => asset.name === "changelog.md") - - if (bundle) { - const response = await axios.get(bundle.browser_download_url) - .catch(() => null) - - if (response) { - changelogData = response.data - } - } - - releasesNotes.push({ - version: release.tag_name, - date: release.published_at, - body: changelogData, - isMd: bundle !== undefined - }) - } - - return res.json(releasesNotes) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/endpoints/globalServerLimits.js b/packages/server/services/main/controllers/PublicController/endpoints/globalServerLimits.js deleted file mode 100755 index f0701535..00000000 --- a/packages/server/services/main/controllers/PublicController/endpoints/globalServerLimits.js +++ /dev/null @@ -1,27 +0,0 @@ -import { ServerLimit } from "@db_models" - -export default { - method: "GET", - route: "/global_server_limits/:limitkey", - fn: async (req, res) => { - const { limitkey } = req.params - - const serverLimit = await ServerLimit.findOne({ - key: limitkey, - active: true, - }) - .catch(err => { - return res.status(500).json({ - error: err.message - }) - }) - - if (!serverLimit) { - return res.status(404).json({ - error: "Server limit not found or inactive" - }) - } - - return res.json(serverLimit) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/endpoints/ping.js b/packages/server/services/main/controllers/PublicController/endpoints/ping.js deleted file mode 100755 index 324631bf..00000000 --- a/packages/server/services/main/controllers/PublicController/endpoints/ping.js +++ /dev/null @@ -1,7 +0,0 @@ -export default { - route: "/ping", - method: "GET", - fn: async (req, res) => { - return res.send("pong") - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/endpoints/postingPolicy.js b/packages/server/services/main/controllers/PublicController/endpoints/postingPolicy.js deleted file mode 100755 index 74169649..00000000 --- a/packages/server/services/main/controllers/PublicController/endpoints/postingPolicy.js +++ /dev/null @@ -1,9 +0,0 @@ -export default { - method: "GET", - route: "/posting_policy", - middlewares: ["withOptionalAuthentication"], - fn: async (req, res) => { - // TODO: Use `PermissionsAPI` to get the user's permissions and return the correct policy, by now it will return the default policy - return res.json(global.DEFAULT_POSTING_POLICY) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/endpoints/serverHealth.js b/packages/server/services/main/controllers/PublicController/endpoints/serverHealth.js deleted file mode 100755 index caa02e90..00000000 --- a/packages/server/services/main/controllers/PublicController/endpoints/serverHealth.js +++ /dev/null @@ -1,29 +0,0 @@ -const os = require("os") - -function getUsage() { - let usage = process.cpuUsage() - - usage.time = process.uptime() * 1000 - usage.percent = (usage.system + usage.user) / (usage.time * 10) - - return usage -} - -export default { - method: "GET", - route: "/server/health", - fn: async (req, res) => { - const cpus = os.cpus() - - // get process info, memory usage, etc - const processInfo = { - memoryUsage: process.memoryUsage(), - cpuUsage: getUsage(), - uptime: process.uptime(), - memoryUsage: process.memoryUsage(), - cpus: cpus, - } - - return res.json(processInfo) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/incidentPrediction.js b/packages/server/services/main/controllers/PublicController/incidentPrediction.js deleted file mode 100755 index 3acdff5a..00000000 --- a/packages/server/services/main/controllers/PublicController/incidentPrediction.js +++ /dev/null @@ -1,27 +0,0 @@ -// import { Schematized } from "../../../lib" -// import IndecentPrediction from "../../../utils/indecent-prediction" - -// export default { -// method: "GET", -// route: "/indecent_prediction", -// fn: Schematized({ -// select: ["url"], -// required: ["url"], -// }, async (req, res) => { -// const { url } = req.selection - -// const predictions = await IndecentPrediction({ -// url, -// }).catch((err) => { -// res.status(500).json({ -// error: err.message, -// }) - -// return null -// }) - -// if (predictions) { -// return res.json(predictions) -// } -// }) -// } \ No newline at end of file diff --git a/packages/server/services/main/controllers/PublicController/index.js b/packages/server/services/main/controllers/PublicController/index.js deleted file mode 100755 index 3ff1e414..00000000 --- a/packages/server/services/main/controllers/PublicController/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import { Controller } from "linebridge/src/server" -import generateEndpointsFromDir from "linebridge/src/server/lib/generateEndpointsFromDir" - -export default class PublicController extends Controller { - static refName = "PublicController" - - httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints") -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/SearchController/endpoints/getQuickSearch.js b/packages/server/services/main/controllers/SearchController/endpoints/getQuickSearch.js deleted file mode 100755 index 29bf83cd..00000000 --- a/packages/server/services/main/controllers/SearchController/endpoints/getQuickSearch.js +++ /dev/null @@ -1,20 +0,0 @@ -import getMutuals from "@services/getMutuals" - -export default { - method: "GET", - route: "/quick", - middlewares: ["withAuthentication"], - fn: async (req, res) => { - let mutuals = await getMutuals({ - from_user_id: req.user._id.toString(), - }).catch((error) => { - console.error(error) - - return [] - }) - - return res.json({ - friends: mutuals, - }) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/SearchController/endpoints/getSearch.js b/packages/server/services/main/controllers/SearchController/endpoints/getSearch.js deleted file mode 100755 index 933e54d8..00000000 --- a/packages/server/services/main/controllers/SearchController/endpoints/getSearch.js +++ /dev/null @@ -1,86 +0,0 @@ -import { User, Playlist, Track } from "@db_models" -import pmap from "p-map" - -export default { - method: "GET", - route: "/", - middlewares: ["withOptionalAuthentication"], - fn: async (req, res) => { - const { keywords = "", params = {} } = req.query - - let suggestions = {} - - const searchers = [ - { - id: "users", - model: User, - query: { - $or: [ - { username: { $regex: keywords, $options: "i" } }, - { fullName: { $regex: keywords, $options: "i" } }, - ] - }, - limit: params.limit_per_section ?? 5, - select: "username fullName avatar verified", - }, - { - id: "playlists", - model: Playlist, - query: { - $or: [ - { title: { $regex: keywords, $options: "i" } }, - ] - }, - limit: params.limit_per_section ?? 5, - }, - { - id: "tracks", - model: Track, - query: { - $or: [ - { title: { $regex: keywords, $options: "i" } }, - { author: { $regex: keywords, $options: "i" } }, - { album: { $regex: keywords, $options: "i" } }, - ] - }, - limit: params.limit_per_section ?? 5, - } - ] - - let selectedSearchers = [] - - if (!Array.isArray(params.select)) { - selectedSearchers = searchers - } else { - const findedSearchers = [] - - for (const searcher of searchers) { - if (params.select.includes(searcher.id)) { - findedSearchers.push(searcher) - } - } - - selectedSearchers = findedSearchers - } - - await pmap( - selectedSearchers, - async (searcher) => { - let results = await searcher.model.find(searcher.query) - .limit(searcher.limit ?? 5) - .select(searcher.select ?? undefined) - - if (results.length > 0) { - suggestions[searcher.id] = results - } - - return - }, - { - concurrency: 3 - } - ) - - return res.json(suggestions) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/SearchController/index.js b/packages/server/services/main/controllers/SearchController/index.js deleted file mode 100755 index c2d10021..00000000 --- a/packages/server/services/main/controllers/SearchController/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from "linebridge/dist/server" -import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir" - -export default class SearchController extends Controller { - static refName = "SearchController" - static useRoute = "/search" - - httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints") -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/StatusController/endpoints/getConnectedFollowedUsers.js b/packages/server/services/main/controllers/StatusController/endpoints/getConnectedFollowedUsers.js deleted file mode 100755 index 335ad726..00000000 --- a/packages/server/services/main/controllers/StatusController/endpoints/getConnectedFollowedUsers.js +++ /dev/null @@ -1,16 +0,0 @@ -import { Endpoint } from "linebridge/dist/server" -import getConnectedUsersFollowing from "../services/getConnectedUsersFollowing" - -export default class GetConnectedFollowedUsers extends Endpoint { - static method = "GET" - static route = "/connected/following" - static middlewares = ["withAuthentication"] - - async fn(req, res) { - const users = await getConnectedUsersFollowing({ - from_user_id: req.user._id.toString(), - }) - - return res.json(users) - } -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/StatusController/index.js b/packages/server/services/main/controllers/StatusController/index.js deleted file mode 100755 index 6532ba3b..00000000 --- a/packages/server/services/main/controllers/StatusController/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from "linebridge/dist/server" -import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir" - -export default class StatusController extends Controller { - static refName = "StatusController" - static useRoute = "/status" - - httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints") -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/StatusController/services/getConnectedUsersFollowing.js b/packages/server/services/main/controllers/StatusController/services/getConnectedUsersFollowing.js deleted file mode 100755 index b78d51ae..00000000 --- a/packages/server/services/main/controllers/StatusController/services/getConnectedUsersFollowing.js +++ /dev/null @@ -1,28 +0,0 @@ -import { UserFollow } from "@db_models" - -export default async (payload = {}) => { - const { from_user_id, limit = 10, offset = 0 } = payload - - // TODO: Sort by latest history interaction - - // get all the users that are following - let followingUsersIds = await UserFollow.find({ - user_id: from_user_id, - }) - // .skip(offset) - // .limit(limit) - - followingUsersIds = followingUsersIds.map((follow) => { - return follow.to - }) - - const searchResult = await global.engine.ws.find.manyById(followingUsersIds) - - // TODO: Calculate last session duration or last activity at - return searchResult.map((user) => { - return { - _id: user.user_id, - username: user.username, - } - }) -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/index.js b/packages/server/services/main/controllers/index.js deleted file mode 100755 index 48b289cb..00000000 --- a/packages/server/services/main/controllers/index.js +++ /dev/null @@ -1,25 +0,0 @@ -export { default as UserController } from "./UserController" -export { default as SessionController } from "./SessionController" - -export { default as StatusController } from "./StatusController" -export { default as FollowController } from "./FollowController" - -export { default as PostsController } from "./PostsController" -export { default as CommentsController } from "./CommentsController" -export { default as FeedController } from "./FeedController" // Needs to migrate to lb 0.15 - -export { default as StreamingController } from "./StreamingController" - -export { default as BadgesController } from "./BadgesController" -export { default as FeaturedEventsController } from "./FeaturedEventsController" // Needs to migrate to lb 0.15 - -export { default as RolesController } from "./RolesController" // Needs to migrate to lb 0.15 -export { default as SearchController } from "./SearchController" // Needs to migrate to lb 0.15 - -export { default as ModerationController } from "./ModerationController" - -export { default as AdminController } from "./AdminController" - -export { default as AutoUpdateController } from "./AutoUpdate" - -export { default as NFCController } from "./NFCController" \ No newline at end of file diff --git a/packages/server/services/main/fixments/additions_to_attachments.js b/packages/server/services/main/fixments/additions_to_attachments.js deleted file mode 100755 index 58634d7b..00000000 --- a/packages/server/services/main/fixments/additions_to_attachments.js +++ /dev/null @@ -1,26 +0,0 @@ -import { Post } from "@db_models" -import DBManager from "../classes/DbManager" - -async function main() { - const dbManager = new DBManager() - await dbManager.connect() - - const posts = await Post.find({}).catch(() => false) - - for await (let post of posts) { - let postData = post.toObject() - - if (postData["additions"]) { - // transform additions to attachments - postData["attachments"] = postData["additions"] - } - - post.attachments = postData["attachments"] - - await post.save() - } - - console.log("Done!") -} - -main() \ No newline at end of file diff --git a/packages/server/services/main/fixments/migrate_posts_likes.js b/packages/server/services/main/fixments/migrate_posts_likes.js deleted file mode 100755 index 81d3b0bf..00000000 --- a/packages/server/services/main/fixments/migrate_posts_likes.js +++ /dev/null @@ -1,96 +0,0 @@ -require("dotenv").config() - -import fs from "fs" -import path from "path" - -import { Post, PostLike } from "@db_models" -import { performance } from "perf_hooks" -import DBManager from "../classes/DbManager" - -const logBuffer = [] - -function log(...args) { - console.log(...args) - - logBuffer.push(`[${new Date().toISOString()}] | ${args.join(" ")}`) -} - -process.on("unhandledRejection", (reason, promise) => { - log("🆘 Unhandled Rejection at:", promise, "reason:", reason) - - writeLog() - - process.exit(1) -}) - -process.on("uncaughtException", (err) => { - log("🆘 Uncaught Exception at:", err) - - writeLog() - - process.exit(1) -}) - -function writeLog() { - fs.writeFileSync(path.resolve(process.cwd(), `migrate_posts_likes.${new Date().getTime()}.log`), logBuffer.join("\n")) -} - -async function main() { - const dbManager = new DBManager() - await dbManager.connect() - - log(`Starting migration...`) - - const posts = await Post.find({}).catch(() => false) - - log(`✅ Found ${posts.length} posts`) - - for await (let post of posts) { - let postData = post - - log(`➡️ Migrating post likes of [${post._id}]...`) - - const postMigrationStartTime = performance.now() - - if (postData["likes"]) { - // create a PostLike for each like - for await (let like of postData["likes"]) { - try { - // check if the PostLike already exists - let likeObj = await PostLike.findOne({ - post_id: post._id, - user_id: like, - }).catch(() => false) - - if (likeObj) { - log(`🔗 PostLike for [${like}] already exists, skipping...`) - continue - } - - log(`🔗 Creating PostLike for [${like}]...`) - - likeObj = new PostLike({ - post_id: post._id, - user_id: like, - }) - - await likeObj.save() - - log(`🔗✅ Created PostLike for [${like}]`) - } catch (error) { - log(`🔗❌ Error while creating PostLike for [${like}]:`, error) - } - } - } - - const postMigrationEndTime = performance.now() - - log(`⏱️ Post likes migration of [${post._id}] took ${postMigrationEndTime - postMigrationStartTime}ms`) - } - - log(`✅ Migrated ${posts.length} posts`) - - writeLog() -} - -main() \ No newline at end of file diff --git a/packages/server/services/main/fixments/move_playlist_to_release.js b/packages/server/services/main/fixments/move_playlist_to_release.js deleted file mode 100755 index db0a7cae..00000000 --- a/packages/server/services/main/fixments/move_playlist_to_release.js +++ /dev/null @@ -1,47 +0,0 @@ -import { Release, Playlist } from "@db_models" -import DBManager from "@shared-classes/DbManager" - -async function main() { - console.log(`Running fixment move_playlist_to_release...`) - - const dbManager = new DBManager() - await dbManager.initialize() - - const playlists = await Playlist.find({}).catch(() => false) - - console.log(`Found ${playlists.length} playlists`) - - for await (let playlist of playlists) { - console.log(`Moving playlist ${playlist._id} to release...`) - - let data = playlist.toObject() - - let release = await Release.findOne(data).catch((err) => { - return false - }) - - if (release) { - console.log(`Release for playlist ${playlist._id} already exists, skipping...`) - continue - } - - release = new Release({ - user_id: data.user_id, - title: data.title, - type: "album", - list: data.list, - cover: data.cover, - created_at: data.created_at ?? new Date(), - publisher: data.publisher, - public: data.public - }) - - console.log(`Playlist ${playlist._id} done`) - - await release.save() - } - - console.log("Done!") -} - -main() \ No newline at end of file diff --git a/packages/server/services/main/lib/checkUserAdmin/index.js b/packages/server/services/main/lib/checkUserAdmin/index.js deleted file mode 100755 index 3c999e4e..00000000 --- a/packages/server/services/main/lib/checkUserAdmin/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import { User } from "@db_models" - -export default async (user_id) => { - if (!user_id) { - throw new Error("Missing user id") - } - - const user = await User.findById(user_id) - - if (!user) { - throw new Error("User not found") - } - - if (!user.roles.includes("admin")) { - return false - } - - return true -} \ No newline at end of file diff --git a/packages/server/services/main/lib/chunkedUpload/index.js b/packages/server/services/main/lib/chunkedUpload/index.js deleted file mode 100755 index 976a3527..00000000 --- a/packages/server/services/main/lib/chunkedUpload/index.js +++ /dev/null @@ -1,232 +0,0 @@ -import fs from "fs" -import path from "path" -import mime from "mime-types" -import Busboy from "busboy" -import crypto from "crypto" -import { fsMerge } from "split-chunk-merge" - -export default class ChunkedUpload { - constructor(options = {}) { - this.options = options - - this.outputPath = options.outputPath - this.tmpPath = options.tmpPath ?? "/tmp" - - this.maxFileSize = options.maxFileSize ?? 95 - this.acceptedMimeTypes = options.acceptedMimeTypes ?? [ - "image/*", - "video/*", - "audio/*", - ] - - this.strictHashCheck = options.strictHashCheck ?? false - - if (!this.outputPath) { - throw new Error("Missing outputPath") - } - } - - _isLastPart = (contentRange) => { - return contentRange.size === contentRange.end + 1 - } - - _makeSureDirExists = dirName => { - if (!fs.existsSync(dirName)) { - fs.mkdirSync(dirName, { recursive: true }) - } - } - - _buildOriginalFile = async (fileHash, filename) => { - const chunkPartsPath = path.join(this.tmpPath, fileHash) - const mergedFilePath = path.join(this.outputPath, filename) - - let partsFilenames = fs.readdirSync(chunkPartsPath) - - // sort the parts - partsFilenames = partsFilenames.sort((a, b) => { - const aNumber = Number(a) - const bNumber = Number(b) - - if (aNumber < bNumber) { - return -1 - } - - if (aNumber > bNumber) { - return 1 - } - - return 0 - }) - - partsFilenames = partsFilenames.map((partFilename) => { - return path.join(chunkPartsPath, partFilename) - }) - - // merge the parts - await fsMerge(partsFilenames, mergedFilePath) - - // check hash - if (this.strictHashCheck) { - const mergedFileHash = await this._getFileHash(mergedFilePath) - - if (mergedFileHash !== fileHash) { - throw new Error("File hash mismatch") - } - } - - //fs.rmdirSync(chunkPartsPath, { recursive: true }) - - return mergedFilePath - } - - _getFileHash = async (filePath) => { - const buffer = await fs.promises.readFile(filePath) - - const hash = await crypto.createHash("sha256") - .update(buffer) - .digest() - - return hash.toString("hex") - } - - makeMiddleware = () => { - return (req, res, next) => { - const busboy = Busboy({ headers: req.headers }) - - busboy.on("file", async (fieldName, file, info) => { - try { - const fileHash = req.headers["file-hash"] - const chunkNumber = req.chunkNumber = req.headers["file-chunk-number"] - const totalChunks = req.headers["file-total-chunks"] - const fileSize = req.headers["file-size"] - - if (!fileHash) { - return res.status(400).json({ - error: "Missing header [file-hash]", - }) - } - - if (!chunkNumber) { - return res.status(400).json({ - error: "Missing header [file-chunk-number]", - }) - } - - if (!totalChunks) { - return res.status(400).json({ - error: "Missing header [file-total-chunks]", - }) - } - - if (!fileSize) { - return res.status(400).json({ - error: "Missing header [file-size]", - }) - } - - // check if file size is allowed - if (fileSize > this.maxFileSize) { - if (typeof this.options.onExceedMaxFileSize === "function") { - const result = await this.options.onExceedMaxFileSize({ - fileHash, - chunkNumber, - totalChunks, - fileSize, - headers: req.headers, - user: req.user, - }) - - if (!result) { - return res.status(413).json({ - error: "File size is too big", - }) - } - } else { - return res.status(413).json({ - error: "File size is too big", - }) - } - } - - // check if allowedMimeTypes is an array and if it contains the file's mimetype - if (this.acceptedMimeTypes && Array.isArray(this.acceptedMimeTypes)) { - const regex = new RegExp(this.acceptedMimeTypes.join("|").replace(/\*/g, "[a-z]+").replace(/!/g, "^"), "i") - - if (!regex.test(info.mimeType)) { - return res.status(400).json({ - error: "File type is not allowed", - mimeType: info.mimeType, - }) - } - } - - const filePath = path.join(this.tmpPath, fileHash) - const chunkPath = path.join(filePath, chunkNumber) - - this._makeSureDirExists(filePath) - - const writeStream = fs.createWriteStream(chunkPath, { flags: "a" }) - - file.pipe(writeStream) - - file.on("end", async () => { - if (Number(chunkNumber) === totalChunks - 1) { - try { - // build final filename - const realMimeType = mime.lookup(info.filename) - const finalFilenameExtension = mime.extension(realMimeType) - const finalFilename = `${fileHash}.${finalFilenameExtension}` - - const buildResult = await this._buildOriginalFile( - fileHash, - finalFilename, - ) - .catch((err) => { - res.status(500).json({ - error: "Failed to build final file", - }) - - return false - }) - - if (buildResult) { - req.isLastPart = true - req.fileResult = { - fileHash, - filepath: buildResult, - filename: finalFilename, - mimetype: realMimeType, - size: fileSize, - } - - global.cacheService.appendToDeletion(buildResult) - - next() - } - } catch (error) { - return res.status(500).json({ - error: "Failed to build final file", - }) - } - } else { - req.isLastPart = false - - return res.status(200).json({ - message: "Chunk uploaded", - chunkNumber, - }) - } - }) - } catch (error) { - console.log("error:", error) - - return res.status(500).json({ - error: "Failed to upload file", - }) - } - }) - - req.pipe(busboy) - } - } -} \ No newline at end of file diff --git a/packages/server/services/main/lib/index.js b/packages/server/services/main/lib/index.js deleted file mode 100755 index 8a71e94d..00000000 --- a/packages/server/services/main/lib/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as Schematized } from "./schematized" - -export * as Token from "./token" \ No newline at end of file diff --git a/packages/server/services/main/lib/schematized/index.js b/packages/server/services/main/lib/schematized/index.js deleted file mode 100755 index 87ec0030..00000000 --- a/packages/server/services/main/lib/schematized/index.js +++ /dev/null @@ -1,66 +0,0 @@ -export default (schema = {}, fn) => { - return async (req, res, next) => { - if (typeof req.body === "undefined") { - req.body = {} - } - if (typeof req.query === "undefined") { - req.query = {} - } - - if (typeof req.selection !== "object") { - req.selection = {} - } - - if (schema.required) { - if (!Array.isArray(schema.required)) { - console.warn("[INVALID SCHEMA] schema.required is defined but is not an array") - return - } - - const missingKeys = [] - const requiredKeys = Array.isArray(schema.required) ? schema.required : [] - - for (let key of requiredKeys) { - const value = req.body[key] || req.query[key] - - if (!value || typeof value === "undefined") { - missingKeys.push(key) - break - } - - req.selection[key] = value - } - - if (missingKeys.length > 0) { - return res.status(400).json({ - error: `Missing required keys > ${missingKeys}`, - missingKeys: missingKeys - }) - } - } - - if (schema.select) { - if (!Array.isArray(schema.select)) { - console.warn("[INVALID SCHEMA] schema.select is defined but is not an array") - return - } - - // assign objects along request body and query. - for await (let key of schema.select) { - if (req.body && typeof req.body[key] !== "undefined") { - req.selection[key] = req.body[key] - continue - } - - if (req.query && typeof req.query[key] !== "undefined") { - req.selection[key] = req.query[key] - continue - } - } - } - - if (typeof fn === "function") { - return await fn(req, res, next) - } - } -} \ No newline at end of file diff --git a/packages/server/services/main/lib/secureEntry/index.js b/packages/server/services/main/lib/secureEntry/index.js deleted file mode 100755 index e7591da0..00000000 --- a/packages/server/services/main/lib/secureEntry/index.js +++ /dev/null @@ -1,133 +0,0 @@ -import crypto from "crypto" - -export default class SecureEntry { - constructor(model, params = {}) { - this.params = params - - if (!model) { - throw new Error("Missing model") - } - - this.model = model - } - - static get encrytionAlgorithm() { - return "aes-256-cbc" - } - - async set(key, value, { - keyName = "key", - valueName = "value", - }) { - if (!keyName) { - throw new Error("Missing keyName") - } - - if (!valueName) { - throw new Error("Missing valueName") - } - - if (!key) { - throw new Error("Missing key") - } - - if (!value) { - throw new Error("Missing value") - } - - let entry = await this.model.findOne({ - [keyName]: key, - [valueName]: value, - }).catch(() => null) - - const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex") - const iv = crypto.randomBytes(16) - - const cipher = crypto.createCipheriv(SecureEntry.encrytionAlgorithm, encryptionKey, iv) - - let encryptedData - - try { - encryptedData = cipher.update(value) - } - catch (error) { - console.error(error) - } - - encryptedData = Buffer.concat([encryptedData, cipher.final()]) - - value = iv.toString("hex") + ":" + encryptedData.toString("hex") - - if (entry) { - entry[valueName] = value - - await entry.save() - - return entry - } - - entry = new this.model({ - [keyName]: key, - [valueName]: value, - }) - - await entry.save() - - return entry - } - - async get(key, value, { - keyName = "key", - valueName = "value", - }) { - if (!keyName) { - throw new Error("Missing keyName") - } - if (!key) { - throw new Error("Missing key") - } - - const searchQuery = { - [keyName]: key, - } - - if (value) { - searchQuery[valueName] = value - } - - const entry = await this.model.findOne(searchQuery).catch(() => null) - - if (!entry || !entry[valueName]) { - return null - } - - const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex") - - const iv = Buffer.from(entry[valueName].split(":")[0], "hex") - const encryptedText = Buffer.from(entry[valueName].split(":")[1], "hex") - - const decipher = crypto.createDecipheriv(SecureEntry.encrytionAlgorithm, encryptionKey, iv) - - let decrypted = decipher.update(encryptedText) - - decrypted = Buffer.concat([decrypted, decipher.final()]) - - return decrypted.toString() - } - - async deleteByID(_id) { - if (!_id) { - throw new Error("Missing _id") - } - - const entry = await this.model.findById(_id).catch(() => null) - - if (!entry) { - return null - } - - await entry.delete() - - return entry - } -} \ No newline at end of file diff --git a/packages/server/services/main/lib/token/index.js b/packages/server/services/main/lib/token/index.js deleted file mode 100755 index 0cfe9b51..00000000 --- a/packages/server/services/main/lib/token/index.js +++ /dev/null @@ -1,223 +0,0 @@ -import jwt from "jsonwebtoken" -import { Session, RegenerationToken } from "@db_models" - -export default class Token { - static async createNewAuthToken(payload, options = {}) { - if (options.updateSession) { - const sessionData = await Session.findOne({ _id: options.updateSession }) - - payload.session_uuid = sessionData.session_uuid - } else { - payload.session_uuid = global.nanoid() - } - - const token = jwt.sign({ - session_uuid: payload.session_uuid, - username: payload.username, - user_id: payload.user_id, - signLocation: payload.signLocation, - }, global.jwtStrategy.secretOrKey, { - expiresIn: global.jwtStrategy.expiresIn ?? "1h", - algorithm: global.jwtStrategy.algorithm ?? "HS256" - }) - - return token - } - - static async validate(token) { - if (typeof token === "undefined") { - throw new Error("Token is undefined") - } - - let result = { - expired: false, - valid: true, - data: null - } - - await jwt.verify(token, global.jwtStrategy.secretOrKey, async (err, decoded) => { - if (err) { - result.valid = false - result.error = err.message - - if (err.message === "jwt expired") { - result.expired = true - } - - return - } - - result = { ...result, ...decoded } - - const sessions = await Session.find({ user_id: result.user_id }) - const sessionsTokens = sessions.map((session) => { - if (session.user_id === result.user_id) { - return session.token - } - }) - - if (!sessionsTokens.includes(token)) { - result.valid = false - result.error = "Session token not found" - } else { - result.valid = true - } - }) - - if (result.valid) { - result.data = await jwt.decode(token) - } - - return result - } - - static async regenerate(expiredToken, refreshToken, aggregateData = {}) { - // search for a regeneration token with the expired token (Should exist only one) - const regenerationToken = await RegenerationToken.findOne({ refreshToken: refreshToken }) - - if (!regenerationToken) { - throw new Error("Cannot find regeneration token") - } - - // check if the regeneration token is valid and not expired - let decodedRefreshToken = null - let decodedExpiredToken = null - - try { - decodedRefreshToken = jwt.decode(refreshToken) - decodedExpiredToken = jwt.decode(expiredToken) - } catch (error) { - console.error(error) - // TODO: Storage this incident - } - - if (!decodedRefreshToken) { - throw new Error("Cannot decode refresh token") - } - - if (!decodedExpiredToken) { - throw new Error("Cannot decode expired token") - } - - // is not needed to verify the expired token, because it suppossed to be expired - - // verify refresh token - await jwt.verify(refreshToken, global.jwtStrategy.secretOrKey, async (err) => { - // check if is expired - if (err) { - if (err.message === "jwt expired") { - // check if server has enabled the enforcement of regeneration token expiration - if (global.jwtStrategy.enforceRegenerationTokenExpiration) { - // delete the regeneration token - await RegenerationToken.deleteOne({ refreshToken: refreshToken }) - - throw new Error("Regeneration token expired and cannot be regenerated due server has enabled enforcement security policy") - } - } - } - - // check if the regeneration token is associated with the expired token - if (decodedRefreshToken.expiredToken !== expiredToken) { - throw new Error("Regeneration token is not associated with the expired token") - } - }) - - // find the session associated with the expired token - const session = await Session.findOne({ token: expiredToken }) - - if (!session) { - throw new Error("Cannot find session associated with the expired token") - } - - // generate a new token - const newToken = await this.createNewAuthToken({ - username: decodedExpiredToken.username, - session_uuid: session.session_uuid, - user_id: decodedExpiredToken.user_id, - ip_address: aggregateData.ip_address, - }, { - updateSession: session._id, - }) - - // delete the regeneration token - await RegenerationToken.deleteOne({ refreshToken: refreshToken }) - - return newToken - } - - static async createAuth(payload, options = {}) { - const token = await this.createNewAuthToken(payload, options) - - const session = { - token: token, - session_uuid: payload.session_uuid, - username: payload.username, - user_id: payload.user_id, - location: payload.signLocation, - ip_address: payload.ip_address, - client: payload.client, - date: new Date().getTime(), - } - - if (options.updateSession) { - await Session.findByIdAndUpdate(options.updateSession, session) - } else { - let newSession = new Session(session) - - await newSession.save() - } - - return token - } - - static async createRegenerative(expiredToken) { - // check if token is only expired, if is corrupted, reject - let decoded = null - - try { - decoded = jwt.decode(expiredToken) - } catch (error) { - console.error(error) - } - - if (!decoded) { - return false - } - - // check if token exists on a session - const sessions = await Session.find({ user_id: decoded.user_id }) - const currentSession = sessions.find((session) => session.token === expiredToken) - - if (!currentSession) { - throw new Error("This token is not associated with any session") - } - - // create a new refresh token and sign it with maximum expiration time of 1 day - const refreshToken = jwt.sign( - { - expiredToken - }, - global.jwtStrategy.secretOrKey, - { - expiresIn: "1d" - } - ) - - // create a new regeneration token and save it - const regenerationToken = new RegenerationToken({ - expiredToken, - refreshToken, - }) - - await regenerationToken.save() - - // return the regeneration token - return regenerationToken - } - - static async getRegenerationToken(expiredToken) { - const regenerationToken = await RegenerationToken.findOne({ expiredToken }) - - return regenerationToken - } -} \ No newline at end of file diff --git a/packages/server/services/main/lib/videoTranscode/index.js b/packages/server/services/main/lib/videoTranscode/index.js deleted file mode 100755 index 5a3b7df9..00000000 --- a/packages/server/services/main/lib/videoTranscode/index.js +++ /dev/null @@ -1,43 +0,0 @@ -import path from "path" - -const ffmpeg = require("fluent-ffmpeg") - -function videoTranscode(originalFilePath, outputPath, options = {}) { - return new Promise((resolve, reject) => { - const filename = path.basename(originalFilePath) - const outputFilename = `${filename.split(".")[0]}_transcoded.${options.format ?? "webm"}` - const outputFilepath = `${outputPath}/${outputFilename}` - - console.debug(`[TRANSCODING] Transcoding ${originalFilePath} to ${outputFilepath}`) - - const onEnd = async () => { - console.debug(`[TRANSCODING] Finished transcode ${originalFilePath} to ${outputFilepath}`) - - return resolve({ - filepath: outputFilepath, - filename: outputFilename, - }) - } - - const onError = (err) => { - console.error(`[TRANSCODING] Transcoding ${originalFilePath} to ${outputFilepath} failed`, err) - - return reject(err) - } - - ffmpeg(originalFilePath) - .audioBitrate(options.audioBitrate ?? 128) - .videoBitrate(options.videoBitrate ?? 1024) - .videoCodec(options.videoCodec ?? "libvpx") - .audioCodec(options.audioCodec ?? "libvorbis") - .format(options.format ?? "webm") - .output(outputFilepath) - .on("error", onError) - .on("end", onEnd) - .run() - }) -} - -export { - videoTranscode, -} \ No newline at end of file diff --git a/packages/server/services/main/main.service.js b/packages/server/services/main/main.service.js index d5d209fb..387c2f93 100755 --- a/packages/server/services/main/main.service.js +++ b/packages/server/services/main/main.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" diff --git a/packages/server/services/main/package.json b/packages/server/services/main/package.json index 27c06dc5..aaa6e4ae 100755 --- a/packages/server/services/main/package.json +++ b/packages/server/services/main/package.json @@ -1,32 +1,7 @@ { "name": "main", "version": "0.60.2", - "license": "MIT", - "proxy": { - "path": "/main", - "port": 3010 - }, "dependencies": { - "@octokit/rest": "^20.0.2", - "@tensorflow/tfjs-node": "^4.17.0", - "axios": "^1.6.7", - "backblaze-b2": "^1.7.0", - "bcrypt": "^5.1.1", - "busboy": "^1.6.0", - "connect-mongo": "^5.1.0", - "content-range": "^2.0.2", - "ioredis": "^5.3.2", - "jsonwebtoken": "^9.0.2", - "linebridge": "^0.16.0", - "luxon": "^3.4.4", - "mime-types": "^2.1.35", - "minio": "^7.1.3", - "moment": "^2.30.1", - "moment-timezone": "^0.5.45", - "mongoose": "^8.1.2", - "normalize-url": "^8.0.0", - "nsfwjs": "^3.0.0", - "p-map": "^4.0.0", - "p-queue": "^7.3.4" + "@octokit/rest": "^20.0.2" } } diff --git a/packages/server/services/main/controllers/NFCController/endpoints/getTagById.js b/packages/server/services/main/routes/nfc/tag/id/[id]/delete.js old mode 100755 new mode 100644 similarity index 55% rename from packages/server/services/main/controllers/NFCController/endpoints/getTagById.js rename to packages/server/services/main/routes/nfc/tag/id/[id]/delete.js index 9eb9b56c..ff00b316 --- a/packages/server/services/main/controllers/NFCController/endpoints/getTagById.js +++ b/packages/server/services/main/routes/nfc/tag/id/[id]/delete.js @@ -1,8 +1,7 @@ -import { User, NFCTag } from "@db_models" +import { NFCTag } from "@db_models" export default { - method: "GET", - route: "/tags/:id", + middlewares: ["withAuthentication"], fn: async (req, res) => { let tag = await NFCTag.findOne({ _id: req.params.id @@ -16,16 +15,14 @@ export default { tag = tag.toObject() - if (req.user) { - if (tag.user_id.toString() === req.user._id.toString()) { - tag.is_owner = true - } + if (tag.user_id.toString() !== req.auth.session.user_id) { + throw new OperationError(403, "You do not own this tag") } - tag.user = await User.findOne({ - _id: tag.user_id + await NFCTag.deleteOne({ + _id: tag._id.toString() }) - return res.json(tag) + return tag } } \ No newline at end of file diff --git a/packages/server/services/main/routes/nfc/tag/id/[id]/get.js b/packages/server/services/main/routes/nfc/tag/id/[id]/get.js new file mode 100644 index 00000000..0190c8c2 --- /dev/null +++ b/packages/server/services/main/routes/nfc/tag/id/[id]/get.js @@ -0,0 +1,27 @@ +import { User, NFCTag } from "@db_models" + +export default async (req, res) => { + let tag = await NFCTag.findOne({ + _id: req.params.id + }) + + if (!tag) { + return res.status(404).json({ + error: "Cannot find tag" + }) + } + + tag = tag.toObject() + + if (req.user) { + if (tag.user_id.toString() === req.auth.session.user_id) { + tag.is_owner = true + } + } + + tag.user = await User.findOne({ + _id: tag.user_id + }) + + return tag +} \ No newline at end of file diff --git a/packages/server/services/main/controllers/NFCController/endpoints/getTags.js b/packages/server/services/main/routes/nfc/tag/my/get.js old mode 100755 new mode 100644 similarity index 64% rename from packages/server/services/main/controllers/NFCController/endpoints/getTags.js rename to packages/server/services/main/routes/nfc/tag/my/get.js index 3758add0..351fa9bc --- a/packages/server/services/main/controllers/NFCController/endpoints/getTags.js +++ b/packages/server/services/main/routes/nfc/tag/my/get.js @@ -1,14 +1,12 @@ import { NFCTag } from "@db_models" export default { - method: "GET", - route: "/tags", middlewares: ["withAuthentication"], fn: async (req, res) => { let tags = await NFCTag.find({ - user_id: req.user.id + user_id: req.auth.session.user_id }) - return res.json(tags) + return tags } } \ No newline at end of file diff --git a/packages/server/services/main/controllers/NFCController/endpoints/registerTag.js b/packages/server/services/main/routes/nfc/tag/register/[serial]/post.js old mode 100755 new mode 100644 similarity index 85% rename from packages/server/services/main/controllers/NFCController/endpoints/registerTag.js rename to packages/server/services/main/routes/nfc/tag/register/[serial]/post.js index e9c9e903..25db4529 --- a/packages/server/services/main/controllers/NFCController/endpoints/registerTag.js +++ b/packages/server/services/main/routes/nfc/tag/register/[serial]/post.js @@ -9,18 +9,18 @@ const allowedUpdateFields = [ ] export default { - method: "POST", - route: "/tag/:serial", middlewares: ["withAuthentication"], fn: async (req, res) => { let tag = await NFCTag.findOne({ serial: req.params.serial }) + const user_id = req.auth.session.user_id + if (!tag) { tag = new NFCTag({ - user_id: req.user._id.toString(), - owner_id: req.user._id.toString(), + user_id: user_id, + owner_id: user_id, serial: req.params.serial, alias: req.body.alias, behavior: req.body.behavior, @@ -34,7 +34,7 @@ export default { tag = tag.toObject() if (req.user) { - if (tag.user_id !== req.user._id.toString()) { + if (tag.user_id !== user_id) { return res.status(403).json({ error: `You do not own this tag` }) @@ -58,6 +58,6 @@ export default { } } - return res.json(tag) + return tag } } \ No newline at end of file diff --git a/packages/server/services/main/controllers/NFCController/endpoints/getTagBySerial.js b/packages/server/services/main/routes/nfc/tag/serial/get.js old mode 100755 new mode 100644 similarity index 78% rename from packages/server/services/main/controllers/NFCController/endpoints/getTagBySerial.js rename to packages/server/services/main/routes/nfc/tag/serial/get.js index 3d40408d..5fdc1de0 --- a/packages/server/services/main/controllers/NFCController/endpoints/getTagBySerial.js +++ b/packages/server/services/main/routes/nfc/tag/serial/get.js @@ -1,8 +1,6 @@ import { User, NFCTag } from "@db_models" export default { - method: "GET", - route: "/tag/serial/:serial", middlewares: ["withOptionalAuthentication"], fn: async (req, res) => { let tag = await NFCTag.findOne({ @@ -18,7 +16,7 @@ export default { tag = tag.toObject() if (req.user) { - if (tag.user_id.toString() === req.user._id.toString()) { + if (tag.user_id.toString() === req.auth.session.user_id) { tag.is_owner = true } } @@ -27,6 +25,6 @@ export default { _id: tag.user_id }) - return res.json(tag) + return tag } } \ No newline at end of file diff --git a/packages/server/services/main/routes/repo/auto-update/mobile/get.js b/packages/server/services/main/routes/repo/auto-update/mobile/get.js new file mode 100644 index 00000000..f47985bd --- /dev/null +++ b/packages/server/services/main/routes/repo/auto-update/mobile/get.js @@ -0,0 +1,31 @@ +import { Octokit } from "@octokit/rest" + +const octokit = new Octokit({}) + +export default async (req, res) => { + if (!process.env.GITHUB_REPO) { + return res.status(400).json({ + error: "GITHUB_REPO env variable not set" + }) + } + + const lastRelease = await octokit.repos.getLatestRelease({ + owner: process.env.GITHUB_REPO.split("/")[0], + repo: process.env.GITHUB_REPO.split("/")[1] + }) + + const bundle = lastRelease.data.assets.find((asset) => asset.name === "mobile_dist.zip") + const version = lastRelease.data.tag_name + + if (!bundle) { + return res.status(400).json({ + error: "mobile asset not available", + version: version, + }) + } + + return res.json({ + url: bundle.browser_download_url, + version: version, + }) +} \ No newline at end of file diff --git a/packages/server/services/main/routes/repo/releases-notes/get.js b/packages/server/services/main/routes/repo/releases-notes/get.js new file mode 100644 index 00000000..34b974cf --- /dev/null +++ b/packages/server/services/main/routes/repo/releases-notes/get.js @@ -0,0 +1,45 @@ +import { Octokit } from "@octokit/rest" +import axios from "axios" + +const octokit = new Octokit({}) + +export default async (req, res) => { + if (!process.env.GITHUB_REPO) { + return res.status(400).json({ + error: "GITHUB_REPO env variable not set" + }) + } + + const releasesNotes = [] + + // fetch the 3 latest releases + const releases = await octokit.repos.listReleases({ + owner: process.env.GITHUB_REPO.split("/")[0], + repo: process.env.GITHUB_REPO.split("/")[1], + per_page: 3 + }) + + for await (const release of releases.data) { + let changelogData = release.body + + const bundle = release.assets.find((asset) => asset.name === "changelog.md") + + if (bundle) { + const response = await axios.get(bundle.browser_download_url) + .catch(() => null) + + if (response) { + changelogData = response.data + } + } + + releasesNotes.push({ + version: release.tag_name, + date: release.published_at, + body: changelogData, + isMd: bundle !== undefined + }) + } + + return res.json(releasesNotes) +} \ No newline at end of file diff --git a/packages/server/services/main/services/fetchRemoteStreams/index.js b/packages/server/services/main/services/fetchRemoteStreams/index.js deleted file mode 100755 index d2ee5e77..00000000 --- a/packages/server/services/main/services/fetchRemoteStreams/index.js +++ /dev/null @@ -1,78 +0,0 @@ -import axios from "axios" - -import { StreamingCategory, StreamingProfile, User } from "@db_models" - -import composeStreamingSources from "@utils/compose-streaming-sources" -import lodash from "lodash" - -const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? "" -const streamingServerAPIUri = `${streamingServerAPIAddress.startsWith("https") ? "https" : "http"}://${streamingServerAPIAddress.split("://")[1]}` - -export default async (stream_id) => { - let apiURI = `${streamingServerAPIUri}/streams` - - // fetch all streams from api - let { data } = await axios({ - method: "GET", - url: apiURI, - params: { - stream: stream_id, - } - }).catch((err) => { - console.error(err) - return false - }) - - let streamings = [] - - if (!data) return streamings - - if (stream_id) { - streamings.push(data) - } else { - streamings = data - } - - streamings = streamings.map(async (entry) => { - const { stream, profile_id } = entry - - let profile = await StreamingProfile.findById(profile_id) - - if (!profile) return null - - profile = profile.toObject() - - profile._id = profile._id.toString() - - profile.info.category = await StreamingCategory.findOne({ - key: profile.info.category - }) - - let user = await User.findById(profile.user_id) - - if (!user) return null - - user = user.toObject() - - const sources = composeStreamingSources(user.username, profile._id) - - return { - profile_id: profile._id, - info: profile.info, - name: stream, - streamUrl: `${user.username}?profile=${profile._id}`, - sources: lodash.pick(sources, ["rtmp", "hls", "flv", "aac"]), - user, - } - }) - - streamings = await Promise.all(streamings) - - streamings = streamings.filter((stream) => stream !== null) - - if (stream_id) { - return streamings[0] - } - - return streamings -} \ No newline at end of file diff --git a/packages/server/services/main/services/getMutuals/index.js b/packages/server/services/main/services/getMutuals/index.js deleted file mode 100755 index 36eaabd9..00000000 --- a/packages/server/services/main/services/getMutuals/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import { User, UserFollow } from "@db_models" - -// TODO: Sort follows by last activity -export default async (payload = {}) => { - const from_user_id = payload.from_user_id - - if (!from_user_id) { - throw new Error("Missing from_user_id") - } - - let mutuals = [] - - // load friends and recents users - const follows = await UserFollow.find({ - user_id: from_user_id, - }) - .limit(15) - - mutuals = await UserFollow.find({ - user_id: follows.map((follow) => follow.to.toString()), - to: from_user_id, - }) - - // load data user - mutuals = await User.find({ - _id: mutuals.map((mutual) => mutual.user_id.toString()), - }) - - return mutuals -} \ No newline at end of file diff --git a/packages/server/services/main/services/newStreamingProfile/index.js b/packages/server/services/main/services/newStreamingProfile/index.js deleted file mode 100755 index d22ff253..00000000 --- a/packages/server/services/main/services/newStreamingProfile/index.js +++ /dev/null @@ -1,28 +0,0 @@ -import { StreamingProfile } from "@db_models" - -export default async (profile = {}) => { - if (!profile.user_id) { - throw new Error("Invalid request, missing user_id") - } - - if (!profile.profile_name) { - throw new Error("Invalid request, missing profile_name") - } - - const newProfile = new StreamingProfile({ - user_id: profile.user_id, - profile_name: profile.profile_name, - stream_key: global.nanoid(), - info: { - title: "Untitled", - description: "No description", - category: "other", - thumbnail: null, - ...profile.info, - } - }) - - await newProfile.save() - - return newProfile -} \ No newline at end of file diff --git a/packages/server/services/main/utils/aggregate-error/index.js b/packages/server/services/main/utils/aggregate-error/index.js deleted file mode 100755 index 3ae0e5c2..00000000 --- a/packages/server/services/main/utils/aggregate-error/index.js +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Sindre Sorhus (https://sindresorhus.com) -import indentString from '../indent-string'; -import cleanStack from '../clean-stack'; - -const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); - -export default class AggregateError extends Error { - #errors; - - name = 'AggregateError'; - - constructor(errors) { - if (!Array.isArray(errors)) { - throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); - } - - errors = errors.map(error => { - if (error instanceof Error) { - return error; - } - - if (error !== null && typeof error === 'object') { - // Handle plain error objects with message property and/or possibly other metadata - return Object.assign(new Error(error.message), error); - } - - return new Error(error); - }); - - let message = errors - .map(error => { - // The `stack` property is not standardized, so we can't assume it exists - return typeof error.stack === 'string' && error.stack.length > 0 ? cleanInternalStack(cleanStack(error.stack)) : String(error); - }) - .join('\n'); - message = '\n' + indentString(message, 4); - super(message); - - this.#errors = errors; - } - - get errors() { - return this.#errors.slice(); - } -} \ No newline at end of file diff --git a/packages/server/services/main/utils/clean-stack/index.js b/packages/server/services/main/utils/clean-stack/index.js deleted file mode 100755 index 01dfcfce..00000000 --- a/packages/server/services/main/utils/clean-stack/index.js +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Sindre Sorhus (https://sindresorhus.com) -import os from 'os'; -import escapeStringRegexp from '../escape-string-regexp'; - -const extractPathRegex = /\s+at.*[(\s](.*)\)?/; -const pathRegex = /^(?:(?:(?:node|node:[\w/]+|(?:(?:node:)?internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)(?:\.js)?:\d+:\d+)|native)/; -const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir().replace(/\\/g, '/'); - -export default function cleanStack(stack, {pretty = false, basePath} = {}) { - const basePathRegex = basePath && new RegExp(`(at | \\()${escapeStringRegexp(basePath.replace(/\\/g, '/'))}`, 'g'); - - if (typeof stack !== 'string') { - return undefined; - } - - return stack.replace(/\\/g, '/') - .split('\n') - .filter(line => { - const pathMatches = line.match(extractPathRegex); - if (pathMatches === null || !pathMatches[1]) { - return true; - } - - const match = pathMatches[1]; - - // Electron - if ( - match.includes('.app/Contents/Resources/electron.asar') || - match.includes('.app/Contents/Resources/default_app.asar') || - match.includes('node_modules/electron/dist/resources/electron.asar') || - match.includes('node_modules/electron/dist/resources/default_app.asar') - ) { - return false; - } - - return !pathRegex.test(match); - }) - .filter(line => line.trim() !== '') - .map(line => { - if (basePathRegex) { - line = line.replace(basePathRegex, '$1'); - } - - if (pretty) { - line = line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~'))); - } - - return line; - }) - .join('\n'); -} \ No newline at end of file diff --git a/packages/server/services/main/utils/compose-streaming-sources/index.js b/packages/server/services/main/utils/compose-streaming-sources/index.js deleted file mode 100755 index 29214462..00000000 --- a/packages/server/services/main/utils/compose-streaming-sources/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? "" - -const streamingServerAPIUri = `${streamingServerAPIAddress.startsWith("https") ? "https" : "http"}://${streamingServerAPIAddress.split("://")[1]}` - -export default (username, profile_id) => { - const streamId = `${username}${profile_id ? `__${profile_id}` : ""}` - - return { - ingest: process.env.STREAMING_INGEST_SERVER, - rtmp: `${streamingServerAPIUri}/${streamId}`, - rtsp: `rtsp://${process.env.STREAMING_INGEST_SERVER}:8554/live/${streamId}`, - hls: `${streamingServerAPIUri}/stream/hls/${streamId}`, - flv: `${streamingServerAPIUri}/stream/flv/${streamId}`, - mp3: `${streamingServerAPIUri}/stream/mp3/${streamId}`, - } -} \ No newline at end of file diff --git a/packages/server/services/main/utils/createTokenRegeneration/index.js b/packages/server/services/main/utils/createTokenRegeneration/index.js deleted file mode 100755 index 993fb901..00000000 --- a/packages/server/services/main/utils/createTokenRegeneration/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import Token from "@lib/token" - -export default async (expiredToken) => { - let regenerationToken = null - - // check if this expired token has a regeneration token associated - const associatedRegenerationToken = await Token.getRegenerationToken(expiredToken) - - if (associatedRegenerationToken) { - regenerationToken = associatedRegenerationToken.refreshToken - } else { - // create a new regeneration token with the expired token - regenerationToken = await Token.createRegenerative(expiredToken) - } - - return regenerationToken.refreshToken -} \ No newline at end of file diff --git a/packages/server/services/main/utils/download-file/index.js b/packages/server/services/main/utils/download-file/index.js deleted file mode 100755 index 5c51c07f..00000000 --- a/packages/server/services/main/utils/download-file/index.js +++ /dev/null @@ -1,39 +0,0 @@ -import fs from "fs" -import path from "path" -import axios from "axios" - -export default async (payload) => { - return new Promise(async (resolve, reject) => { - try { - let { url, destination } = payload - - // if destination path is not provided, use cache folder - if (!destination) { - destination = path.resolve(global.uploadCachePath, path.basename(url)) - } - - const writer = fs.createWriteStream(destination) - - if (!writer) { - return false - } - - const response = await axios({ - url, - method: "GET", - responseType: "stream" - }) - - response.data.pipe(writer) - - writer.on("finish", () => resolve({ - destination, - delete: () => fs.unlinkSync(destination), - read: () => fs.readFileSync(destination), - })) - writer.on("error", reject) - } catch (error) { - reject(error) - } - }) -} \ No newline at end of file diff --git a/packages/server/services/main/utils/escape-string-regexp/index.js b/packages/server/services/main/utils/escape-string-regexp/index.js deleted file mode 100755 index d49baa45..00000000 --- a/packages/server/services/main/utils/escape-string-regexp/index.js +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Sindre Sorhus (https://sindresorhus.com) -export default function escapeStringRegexp(string) { - if (typeof string !== 'string') { - throw new TypeError('Expected a string'); - } - - // Escape characters with special meaning either inside or outside character sets. - // Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar. - return string - .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') - .replace(/-/g, '\\x2d'); -} \ No newline at end of file diff --git a/packages/server/services/main/utils/fullfillPostsData/index.js b/packages/server/services/main/utils/fullfillPostsData/index.js deleted file mode 100755 index 33abd8fc..00000000 --- a/packages/server/services/main/utils/fullfillPostsData/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import { User, Comment, PostLike, SavedPost } from "@db_models" - -export default async (payload) => { - let { - posts, - for_user_id, - } = payload - - if (!Array.isArray(posts)) { - posts = [posts] - } - - let savedPostsIds = [] - - if (for_user_id) { - const savedPosts = await SavedPost.find({ user_id: for_user_id }) - .sort({ saved_at: -1 }) - - savedPostsIds = savedPosts.map((savedPost) => savedPost.post_id) - } - - let [usersData, likesData, commentsData] = await Promise.all([ - User.find({ - _id: { - $in: posts.map((post) => post.user_id) - } - }), - PostLike.find({ - post_id: { - $in: posts.map((post) => post._id) - } - }).catch(() => []), - Comment.find({ - parent_id: { - $in: posts.map((post) => post._id) - } - }).catch(() => []), - ]) - - // wrap likesData by post_id - likesData = likesData.reduce((acc, like) => { - if (!acc[like.post_id]) { - acc[like.post_id] = [] - } - - acc[like.post_id].push(like) - - return acc - }, {}) - - // wrap commentsData by post_id - commentsData = commentsData.reduce((acc, comment) => { - if (!acc[comment.parent_id]) { - acc[comment.parent_id] = [] - } - - acc[comment.parent_id].push(comment) - - return acc - }, {}) - - posts = await Promise.all(posts.map(async (post, index) => { - post = post.toObject() - - let user = usersData.find((user) => user._id.toString() === post.user_id.toString()) - - if (!user) { - user = { - username: "Deleted user", - } - } - - let likes = likesData[post._id.toString()] ?? [] - - post.countLikes = likes.length - - let comments = commentsData[post._id.toString()] ?? [] - - post.countComments = comments.length - - if (for_user_id) { - post.isLiked = likes.some((like) => like.user_id.toString() === for_user_id) - post.isSaved = savedPostsIds.includes(post._id.toString()) - } - - return { - ...post, - comments: comments.map((comment) => comment._id.toString()), - user, - } - })) - - return posts -} \ No newline at end of file diff --git a/packages/server/services/main/utils/image-byte-array/index.js b/packages/server/services/main/utils/image-byte-array/index.js deleted file mode 100755 index 4c1e6454..00000000 --- a/packages/server/services/main/utils/image-byte-array/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export default (image, numChannels) => { - const pixels = image.data - const numPixels = image.width * image.height - - const values = new Int32Array(numPixels * numChannels) - - for (let i = 0; i < numPixels; i++) { - for (let channel = 0; channel < numChannels; ++channel) { - values[i * numChannels + channel] = pixels[i * 4 + channel] - } - } - - return values -} \ No newline at end of file diff --git a/packages/server/services/main/utils/indecent-prediction/index.js b/packages/server/services/main/utils/indecent-prediction/index.js deleted file mode 100755 index 1749c440..00000000 --- a/packages/server/services/main/utils/indecent-prediction/index.js +++ /dev/null @@ -1,73 +0,0 @@ -const tf = require("@tensorflow/tfjs-node") - -import * as nsfwjs from "nsfwjs/dist" -import sharp from "sharp" - -import fs from "fs" -import path from "path" - -import downloadFile from "../download-file" -import readImage from "../read-image" -import imageByteArray from "../image-byte-array" - -const imageToInput = (image, numChannels) => { - const values = imageByteArray(image, numChannels) - const outShape = [image.height, image.width, numChannels] - const input = tf.tensor3d(values, outShape, "int32") - - return input -} - -if (global.isProduction) { - tf.enableProdMode() -} - -export default async (payload) => { - try { - let { url, image, channels = 3 } = payload - - let file = null - const model = await nsfwjs.load() - - if (!image && url) { - file = await downloadFile({ url }) - - image = file.destination - } - - // check if image is not a jpg - if (image.indexOf(".jpg") === -1) { - // convert image to jpg - const converted = await sharp(image) - .jpeg() - .toBuffer() - - // write converted image to disk (use cache) - const destination = path.resolve(global.uploadCachePath, `${Date.now()}.jpg`) - - fs.writeFileSync(destination, converted) - - // set image to the converted image - file = { - destination, - delete: () => fs.unlinkSync(destination), - } - - image = destination - } - - const logo = readImage(image) - const input = imageToInput(logo, channels) - - const predictions = await model.classify(input) - - if (typeof file.delete === "function") { - await file.delete() - } - - return predictions - } catch (error) { - console.error(`Failed to process image >`, error) - console.trace() - } -} \ No newline at end of file diff --git a/packages/server/services/main/utils/indent-string/index.js b/packages/server/services/main/utils/indent-string/index.js deleted file mode 100755 index eafd265a..00000000 --- a/packages/server/services/main/utils/indent-string/index.js +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Sindre Sorhus (https://sindresorhus.com) -export default function indentString(string, count = 1, options = {}) { - const { - indent = ' ', - includeEmptyLines = false - } = options; - - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); - } - - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } - - if (count < 0) { - throw new RangeError( - `Expected \`count\` to be at least 0, got \`${count}\`` - ); - } - - if (typeof indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof indent}\`` - ); - } - - if (count === 0) { - return string; - } - - const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; - - return string.replace(regex, indent.repeat(count)); -} \ No newline at end of file diff --git a/packages/server/services/main/utils/is-nsfw/index.js b/packages/server/services/main/utils/is-nsfw/index.js deleted file mode 100755 index 538ac704..00000000 --- a/packages/server/services/main/utils/is-nsfw/index.js +++ /dev/null @@ -1,22 +0,0 @@ -const minumunPredictions = { - "Hentai": 0.8, - "Porn": 0.7, -} - -export default (predictions) => { - if (!Array.isArray(predictions)) { - throw new Error("predictions must be an array") - } - - let isNsfw = false - - Object.keys(minumunPredictions).forEach((key) => { - const prediction = predictions.find((prediction) => prediction.className === key) - - if (prediction && prediction.probability >= minumunPredictions[key]) { - isNsfw = true - } - }) - - return isNsfw -} \ No newline at end of file diff --git a/packages/server/services/main/utils/pMap/index.js b/packages/server/services/main/utils/pMap/index.js deleted file mode 100755 index 7a47c36c..00000000 --- a/packages/server/services/main/utils/pMap/index.js +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) Sindre Sorhus (https://sindresorhus.com) -import AggregateError from "../aggregate-error"; - -/** -An error to be thrown when the request is aborted by AbortController. -DOMException is thrown instead of this Error when DOMException is available. -*/ -export class AbortError extends Error { - constructor(message) { - super(); - this.name = "AbortError"; - this.message = message; - } -} - -/** -TODO: Remove AbortError and just throw DOMException when targeting Node 18. -*/ -const getDOMException = errorMessage => globalThis.DOMException === undefined - ? new AbortError(errorMessage) - : new DOMException(errorMessage); - -/** -TODO: Remove below function and just "reject(signal.reason)" when targeting Node 18. -*/ -const getAbortedReason = signal => { - const reason = signal.reason === undefined - ? getDOMException("This operation was aborted.") - : signal.reason; - - return reason instanceof Error ? reason : getDOMException(reason); -}; - -export default async function pMap( - iterable, - mapper, - { - concurrency = Number.POSITIVE_INFINITY, - stopOnError = true, - signal, - } = {}, -) { - return new Promise((resolve, reject_) => { - if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) { - throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`); - } - - if (typeof mapper !== "function") { - throw new TypeError("Mapper function is required"); - } - - if (!((Number.isSafeInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`); - } - - const result = []; - const errors = []; - const skippedIndexesMap = new Map(); - let isRejected = false; - let isResolved = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; - const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator](); - - const reject = reason => { - isRejected = true; - isResolved = true; - reject_(reason); - }; - - if (signal) { - if (signal.aborted) { - reject(getAbortedReason(signal)); - } - - signal.addEventListener("abort", () => { - reject(getAbortedReason(signal)); - }); - } - - const next = async () => { - if (isResolved) { - return; - } - - const nextItem = await iterator.next(); - - const index = currentIndex; - currentIndex++; - - // Note: `iterator.next()` can be called many times in parallel. - // This can cause multiple calls to this `next()` function to - // receive a `nextItem` with `done === true`. - // The shutdown logic that rejects/resolves must be protected - // so it runs only one time as the `skippedIndex` logic is - // non-idempotent. - if (nextItem.done) { - isIterableDone = true; - - if (resolvingCount === 0 && !isResolved) { - if (!stopOnError && errors.length > 0) { - reject(new AggregateError(errors)); - return; - } - - isResolved = true; - - if (skippedIndexesMap.size === 0) { - resolve(result); - return; - } - - const pureResult = []; - - // Support multiple `pMapSkip`"s. - for (const [index, value] of result.entries()) { - if (skippedIndexesMap.get(index) === pMapSkip) { - continue; - } - - pureResult.push(value); - } - - resolve(pureResult); - } - - return; - } - - resolvingCount++; - - // Intentionally detached - (async () => { - try { - const element = await nextItem.value; - - if (isResolved) { - return; - } - - const value = await mapper(element, index); - - // Use Map to stage the index of the element. - if (value === pMapSkip) { - skippedIndexesMap.set(index, value); - } - - result[index] = value; - - resolvingCount--; - await next(); - } catch (error) { - if (stopOnError) { - reject(error); - } else { - errors.push(error); - resolvingCount--; - - // In that case we can"t really continue regardless of `stopOnError` state - // since an iterable is likely to continue throwing after it throws once. - // If we continue calling `next()` indefinitely we will likely end up - // in an infinite loop of failed iteration. - try { - await next(); - } catch (error) { - reject(error); - } - } - } - })(); - }; - - // Create the concurrent runners in a detached (non-awaited) - // promise. We need this so we can await the `next()` calls - // to stop creating runners before hitting the concurrency limit - // if the iterable has already been marked as done. - // NOTE: We *must* do this for async iterators otherwise we"ll spin up - // infinite `next()` calls by default and never start the event loop. - (async () => { - for (let index = 0; index < concurrency; index++) { - try { - // eslint-disable-next-line no-await-in-loop - await next(); - } catch (error) { - reject(error); - break; - } - - if (isIterableDone || isRejected) { - break; - } - } - })(); - }); -} - -export const pMapSkip = Symbol("skip"); \ No newline at end of file diff --git a/packages/server/services/main/utils/read-image/index.js b/packages/server/services/main/utils/read-image/index.js deleted file mode 100755 index cf34758e..00000000 --- a/packages/server/services/main/utils/read-image/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import fs from "fs" -import jpeg from "jpeg-js" - -export default (path) => { - const buf = fs.readFileSync(path) - const pixels = jpeg.decode(buf, true) - - return pixels -} \ No newline at end of file diff --git a/packages/server/services/marketplace/marketplace.service.js b/packages/server/services/marketplace/marketplace.service.js index d18bad45..7356ec43 100755 --- a/packages/server/services/marketplace/marketplace.service.js +++ b/packages/server/services/marketplace/marketplace.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" import RedisClient from "@shared-classes/RedisClient" diff --git a/packages/server/services/marketplace/package.json b/packages/server/services/marketplace/package.json index 5ae41dc4..4aaff239 100755 --- a/packages/server/services/marketplace/package.json +++ b/packages/server/services/marketplace/package.json @@ -1,37 +1,9 @@ { "name": "marketplace", "version": "0.60.2", - "license": "MIT", "dependencies": { "7zip-min": "^1.4.4", - "@foxify/events": "^2.1.0", "@octokit/rest": "^19.0.7", - "axios": "^1.2.5", - "bcrypt": "^5.1.0", - "busboy": "^1.6.0", - "comty.js": "^0.58.2", - "connect-mongo": "^4.6.0", - "content-range": "^2.0.2", - "dotenv": "^16.0.3", - "form-data": "^4.0.0", - "formidable": "^2.1.1", - "hyper-express": "^6.5.9", - "jsonwebtoken": "^9.0.0", - "linebridge": "0.15.12", - "live-directory": "^3.0.3", - "luxon": "^3.2.1", - "merge-files": "^0.1.2", - "mime-types": "^2.1.35", - "minio": "^7.0.32", - "moment": "^2.29.4", - "moment-timezone": "^0.5.40", - "mongoose": "^6.9.0", - "normalize-url": "^8.0.0", - "p-map": "^6.0.0", - "p-queue": "^7.3.4", - "redis": "^4.6.6", - "sharp": "^0.31.3", - "split-chunk-merge": "^1.0.0", "sucrase": "^3.32.0", "uglify-js": "^3.17.4" } diff --git a/packages/server/services/music/classes/track/methods/create.js b/packages/server/services/music/classes/track/methods/create.js index 71f8e45f..2f0d25ac 100644 --- a/packages/server/services/music/classes/track/methods/create.js +++ b/packages/server/services/music/classes/track/methods/create.js @@ -26,7 +26,7 @@ export default async (payload = {}) => { mimeType: headers["content-type"], }) - const metadata = { + let metadata = { format: fileMetadata.format.codec, channels: fileMetadata.format.numberOfChannels, sampleRate: fileMetadata.format.sampleRate, diff --git a/packages/server/services/music/controllers/playlists/routes/get/self.js b/packages/server/services/music/controllers/playlists/routes/get/self.js index 5d35ea66..0af4af23 100755 --- a/packages/server/services/music/controllers/playlists/routes/get/self.js +++ b/packages/server/services/music/controllers/playlists/routes/get/self.js @@ -24,8 +24,8 @@ export default async (req, res) => { } } - const playlistsCount = await Playlist.count(searchQuery) - const releasesCount = await Release.count(searchQuery) + const playlistsCount = await Playlist.countDocuments(searchQuery) + const releasesCount = await Release.countDocuments(searchQuery) let total_length = playlistsCount + releasesCount diff --git a/packages/server/services/music/controllers/releases/routes/get/self.js b/packages/server/services/music/controllers/releases/routes/get/self.js index 6586db33..a566d69d 100755 --- a/packages/server/services/music/controllers/releases/routes/get/self.js +++ b/packages/server/services/music/controllers/releases/routes/get/self.js @@ -24,7 +24,7 @@ export default async (req, res) => { } } - const total_length = await Release.count(searchQuery) + const total_length = await Release.countDocuments(searchQuery) let releases = await Release.find(searchQuery) .sort({ created_at: -1 }) diff --git a/packages/server/services/music/controllers/releases/routes/get/user/[user_id].js b/packages/server/services/music/controllers/releases/routes/get/user/[user_id].js index a00507e1..c7a64b12 100755 --- a/packages/server/services/music/controllers/releases/routes/get/user/[user_id].js +++ b/packages/server/services/music/controllers/releases/routes/get/user/[user_id].js @@ -8,7 +8,7 @@ export default async (req, res) => { const { user_id } = req.params const { keywords, limit = 10, offset = 0 } = req.query - const total_length = await Release.count({ + const total_length = await Release.countDocuments({ user_id, }) diff --git a/packages/server/services/music/controllers/tracks/routes/get/liked.js b/packages/server/services/music/controllers/tracks/routes/get/liked.js index 8a2b661b..6413e760 100755 --- a/packages/server/services/music/controllers/tracks/routes/get/liked.js +++ b/packages/server/services/music/controllers/tracks/routes/get/liked.js @@ -9,7 +9,7 @@ export default async (req, res) => { const { limit = 100, offset = 0 } = req.query - let totalLikedTracks = await TrackLike.count({ + let totalLikedTracks = await TrackLike.countDocuments({ user_id: req.session.user_id, }) diff --git a/packages/server/services/music/getPlaylistsFromFollowing.js b/packages/server/services/music/getPlaylistsFromFollowing.js deleted file mode 100755 index 9faac13d..00000000 --- a/packages/server/services/music/getPlaylistsFromFollowing.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Playlist, User, UserFollow } from "@db_models" - -export default async (payload) => { - const { - for_user_id, - limit = 20, - skip = 0, - } = payload - - // get post from users that the user follows - const followingUsers = await UserFollow.find({ - user_id: for_user_id - }) - - const followingUserIds = followingUsers.map((followingUser) => followingUser.to) - - const fetchFromUserIds = [ - for_user_id, - ...followingUserIds, - ] - - // firter out the playlists that are not public - let playlists = await Playlist.find({ - user_id: { $in: fetchFromUserIds }, - $or: [ - { public: true }, - ] - }) - .sort({ created_at: -1 }) - .limit(limit) - .skip(skip) - - playlists = Promise.all(playlists.map(async (playlist) => { - playlist = playlist.toObject() - - playlist.type = "playlist" - - return playlist - })) - - return playlists -} \ No newline at end of file diff --git a/packages/server/services/music/getReleasesFromFollowing.js b/packages/server/services/music/getReleasesFromFollowing.js deleted file mode 100755 index bd35e20f..00000000 --- a/packages/server/services/music/getReleasesFromFollowing.js +++ /dev/null @@ -1,40 +0,0 @@ -import { Release, UserFollow } from "@db_models" - -export default async (payload) => { - const { - for_user_id, - limit = 20, - skip = 0, - } = payload - - // get post from users that the user follows - const followingUsers = await UserFollow.find({ - user_id: for_user_id - }) - - const followingUserIds = followingUsers.map((followingUser) => followingUser.to) - - const fetchFromUserIds = [ - for_user_id, - ...followingUserIds, - ] - - // firter out the releases that are not public - let releases = await Release.find({ - user_id: { $in: fetchFromUserIds }, - $or: [ - { public: true }, - ] - }) - .sort({ created_at: -1 }) - .limit(limit) - .skip(skip) - - releases = Promise.all(releases.map(async (release) => { - release = release.toObject() - - return release - })) - - return releases -} \ No newline at end of file diff --git a/packages/server/services/music/music.service.js b/packages/server/services/music/music.service.js index 4b5aca73..33b70587 100755 --- a/packages/server/services/music/music.service.js +++ b/packages/server/services/music/music.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" diff --git a/packages/server/services/music/package.json b/packages/server/services/music/package.json index ca9a3a24..e230c3e0 100755 --- a/packages/server/services/music/package.json +++ b/packages/server/services/music/package.json @@ -1,33 +1,9 @@ { "name": "music", "version": "0.60.2", - "license": "MIT", - "proxyMount": "/music", - "proxy": { - "path": "/main", - "port": 3050 - }, "dependencies": { - "@foxify/events": "^2.1.0", - "axios": "^1.4.0", - "bcrypt": "5.0.1", - "comty.js": "^0.58.2", - "connect-mongo": "^4.6.0", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.2", - "jsonwebtoken": "8.5.1", - "linebridge": "0.15.12", - "luxon": "^3.0.4", - "minio": "^7.0.32", - "moment": "2.29.4", - "moment-timezone": "0.5.37", - "mongoose": "^6.9.0", - "morgan": "^1.10.0", "ms": "^2.1.3", "music-metadata": "^7.14.0", - "openai": "^4.47.2", - "redis": "^4.6.6", - "socket.io": "^4.5.4" + "openai": "^4.47.2" } } diff --git a/packages/server/services/music/routes/music/feed/get.js b/packages/server/services/music/routes/music/feed/get.js index 4957499a..a355ea5f 100644 --- a/packages/server/services/music/routes/music/feed/get.js +++ b/packages/server/services/music/routes/music/feed/get.js @@ -5,7 +5,7 @@ export default async (req) => { const searchQuery = {} - const total_length = await MusicRelease.count(searchQuery) + const total_length = await MusicRelease.countDocuments(searchQuery) let result = await MusicRelease.find(searchQuery) .limit(limit) diff --git a/packages/server/services/music/routes/music/releases/self/get.js b/packages/server/services/music/routes/music/releases/self/get.js index b838b553..bb89b610 100644 --- a/packages/server/services/music/routes/music/releases/self/get.js +++ b/packages/server/services/music/routes/music/releases/self/get.js @@ -39,7 +39,7 @@ export default { } return { - total_length: await MusicRelease.count(searchQuery), + total_length: await MusicRelease.countDocuments(searchQuery), items: releases, } } diff --git a/packages/server/services/music/services/findSpotifyId.js b/packages/server/services/music/services/findSpotifyId.js deleted file mode 100755 index 0dacc812..00000000 --- a/packages/server/services/music/services/findSpotifyId.js +++ /dev/null @@ -1,45 +0,0 @@ -const clearQueryRegexs = [ - // remove titles with (feat. Something) - new RegExp(/\(feat\..*\)/, "gi"), - // remplace $ with S - new RegExp(/\$/, "gi"), - // remove special characters - new RegExp(/[\(\)\[\]\$\&\*\#\@\!\%\+\=\_\-\:\;\'\"\,\.]/, "gi"), - // remove words like "official video", "official audio", "official music video" - new RegExp(/official\s(video|audio|music\svideo)/, "gi"), -] - -export default async ({ - title, - artist, - sessionToken, -} = {}) => { - let query = `${title} artist:${artist}` - - // run clear query regexs - for (const regex of clearQueryRegexs) { - query = query.replace(regex, "") - } - - const { data } = await global.comty.instances.default({ - method: "GET", - headers: { - "Authorization": `Bearer ${sessionToken}`, - }, - params: { - query: query, - type: "track", - }, - url: "/sync/spotify/search", - }).catch((error) => { - console.error(error.response.data) - - return null - }) - - if (!data) { - return null - } - - return data.tracks.items[0].id -} \ No newline at end of file diff --git a/packages/server/services/music/services/getEnhancedLyricsFromTrack.js b/packages/server/services/music/services/getEnhancedLyricsFromTrack.js deleted file mode 100755 index 722e7bbc..00000000 --- a/packages/server/services/music/services/getEnhancedLyricsFromTrack.js +++ /dev/null @@ -1,73 +0,0 @@ -import findSpotifyId from "@services/findSpotifyId" -import { Track } from "@db_models" -import axios from "axios" - -const syncLyricsProvider = process.env.LYRICS_SYNC_PROVIDER || `https://spotify-lyric-api.herokuapp.com` -const canvasProvider = process.env.LYRICS_CANVAS_PROVIDER || `https://c_cdn-test.ragestudio.net/api/canvas` - -export default async (track, { req }) => { - if (typeof track !== "object") { - throw new Error("Track must be an object") - } - - if (!track._id) { - throw new Error("Track must have an _id") - } - - if (!track.lyricsEnabled) { - throw new Error("Track lyrics are not enabled") - } - - let lyricData = { - syncType: null, - lines: null, - canvas_url: null, - } - - if (!track.spotifyId) { - if (!req.session) { - throw new Error("Session not found and track has no spotify id") - } - - const spotifyId = await findSpotifyId({ - title: track.title, - artist: track.artist, - sessionToken: req.sessionToken, - }, { req }) - - if (!spotifyId) { - throw new Error("Track has no spotify id") - } - - track.spotifyId = spotifyId - - await Track.findOneAndUpdate({ - _id: track._id.toString(), - }, { spotifyId }) - } - - let { data } = await axios.get(`${syncLyricsProvider}/?trackid=${track.spotifyId}`) - - lyricData.syncType = data.syncType - lyricData.lines = data.lines - - if (track.videoCanvas) { - lyricData.canvas_url = track.videoCanvas - } else { - try { - const { data } = await axios.get(`${canvasProvider}/${track.spotifyId}`) - - lyricData.canvas_url = data.canvas_url - - await Track.findOneAndUpdate({ - _id: track._id.toString(), - }, { - videoCanvas: data.canvas_url, - }) - } catch (err) { - //console.log(err.response.data) - } - } - - return lyricData -} \ No newline at end of file diff --git a/packages/server/services/music/services/removeTracks.js b/packages/server/services/music/services/removeTracks.js deleted file mode 100755 index eb79f4dd..00000000 --- a/packages/server/services/music/services/removeTracks.js +++ /dev/null @@ -1,49 +0,0 @@ -import { Track } from "@db_models" - -const urlRegex = new RegExp(`^https://(.*?)/(.*)$`) - -export default async (tracksIds) => { - if (typeof tracksIds === "string") { - tracksIds = [tracksIds] - } - - const removedIds = [] - - // find Tracks - const tracks = await Track.find({ - _id: tracksIds, - }) - - for (const track of tracks) { - const match = urlRegex.exec(track.source) - - const bucket = match[2].split("/")[0] - const objectName = match[2].split("/").slice(1).join("/") - - try { - // find on storage and remove - await new Promise((resolve, reject) => { - global.storage.removeObject(bucket, objectName, (err) => { - if (err) { - return reject(err) - } - - return resolve() - }) - }).catch((err) => { - console.error(err) - return false - }) - - // remove from db - await track.remove() - } catch (error) { - console.error(error) - continue - } - - removedIds.push(track._id) - } - - return removedIds -} \ No newline at end of file diff --git a/packages/server/services/music/ws.js b/packages/server/services/music/ws.js deleted file mode 100755 index 63aed8ae..00000000 --- a/packages/server/services/music/ws.js +++ /dev/null @@ -1,117 +0,0 @@ -import socketio from "socket.io" -import generateFnHandler from "@utils/generateFnHandler" - -import withWsAuth from "@middlewares/withWsAuth" - -import RoomsController from "@classes/RoomsController" - -export default class WebsocketServer { - constructor(server) { - this.io = socketio(server, { - cors: { - origin: "*", - methods: ["GET", "POST"], - credentials: true, - } - }) - - if (global.ioAdapter) { - this.io.adapter(global.ioAdapter) - } - - this.RoomsController = new RoomsController(this.io) - - return this - } - - connectionPool = [] - - events = { - - } - - inviteUserToRoom = async (socket, data) => { - try { - // find sockets with matching user_id - const invitedSockets = this.connectionPool.filter((client) => client.userData._id === data.user_id) - - if (invitedSockets.length === 0) { - console.warn(`[${socket.id}][@${socket.userData.username}] cannot invite user ${data.user_id}, user not found in connection pool`) - return socket.emit("error", { - message: `User ${data.user_id} not found`, - }) - } - - for (const invitedSocket of invitedSockets) { - // check if user is already connected to the room - if (invitedSocket.connectedRoomId === data.roomId) { - console.warn(`[${socket.id}][@${socket.userData.username}] cannot invite user ${data.user_id}, user already connected to room ${data.roomId}`) - return false - } - - console.log(`[${socket.id}][@${socket.userData.username}] inviting user ${data.user_id} to room ${data.roomId}`) - - invitedSocket.emit("invite:received", { - roomId: data.roomId, - invitedBy: { - _id: socket.userData._id, - username: socket.userData.username, - fullName: socket.userData.fullName, - avatar: socket.userData.avatar, - }, - }) - } - } catch (error) { - return socket.emit("error", { - message: error.message, - }) - } - } - - initialize = async () => { - this.io.use(withWsAuth) - - this.io.on("connection", (socket) => { - try { - console.log(`[${socket.id}][${socket.userData.username}] connected to hub.`) - - this.connectionPool.push(socket) - - socket.on("disconnect", () => this.events.disconnect) - - // Rooms - socket.on("join:room", (data) => this.RoomsController.connectSocketToRoom(socket, data.room, data.options)) - socket.on("leave:room", (data) => this.RoomsController.disconnectSocketFromRoom(socket, data?.room ?? socket.connectedRoomId, data?.options ?? {})) - socket.on("invite:user", generateFnHandler(this.inviteUserToRoom, socket)) - - socket.on("ping", (callback) => { - callback() - }) - - socket.on("disconnect", (_socket) => { - console.log(`[${socket.id}][@${socket.userData.username}] disconnected to hub.`) - - if (socket.connectedRoomId) { - console.log(`[${socket.id}][@${socket.userData.username}] was connected to room [${socket.connectedRoomId}], leaving...`) - this.RoomsController.disconnectSocketFromRoom(socket) - } - - // remove from connection pool - this.connectionPool = this.connectionPool.filter((client) => client.id !== socket.id) - }) - - Object.entries(this.events).forEach(([event, handler]) => { - socket.on(event, (data) => { - try { - handler(socket, data) - } catch (error) { - console.error(error) - } - }) - }) - } catch (error) { - console.error(error) - } - }) - } -} diff --git a/packages/server/services/notifications/notifications.service.js b/packages/server/services/notifications/notifications.service.js index beb2cfe8..f62aeb40 100644 --- a/packages/server/services/notifications/notifications.service.js +++ b/packages/server/services/notifications/notifications.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" import RedisClient from "@shared-classes/RedisClient" diff --git a/packages/server/services/notifications/package.json b/packages/server/services/notifications/package.json index bcd2c871..b4692cff 100644 --- a/packages/server/services/notifications/package.json +++ b/packages/server/services/notifications/package.json @@ -1,6 +1,4 @@ { "name": "notifications", - "version": "1.0.0", - "main": "index.js", - "license": "MIT" + "version": "1.0.0" } diff --git a/packages/server/services/posts/classes/posts/methods/fullfill.js b/packages/server/services/posts/classes/posts/methods/fullfill.js index 3345b529..a75532ac 100644 --- a/packages/server/services/posts/classes/posts/methods/fullfill.js +++ b/packages/server/services/posts/classes/posts/methods/fullfill.js @@ -73,7 +73,7 @@ export default async (payload = {}) => { } } - post.hasReplies = await Post.count({ reply_to: post._id }) + post.hasReplies = await Post.countDocuments({ reply_to: post._id }) let likes = likesData[post._id.toString()] ?? [] diff --git a/packages/server/services/posts/classes/posts/methods/toggleLike.js b/packages/server/services/posts/classes/posts/methods/toggleLike.js index 8262c8a7..9a0a3661 100644 --- a/packages/server/services/posts/classes/posts/methods/toggleLike.js +++ b/packages/server/services/posts/classes/posts/methods/toggleLike.js @@ -40,7 +40,7 @@ export default async (payload = {}) => { await PostLike.findByIdAndDelete(likeObj._id) } - const count = await PostLike.count({ + const count = await PostLike.countDocuments({ post_id, }) diff --git a/packages/server/services/posts/package.json b/packages/server/services/posts/package.json index e76389ea..56952f06 100644 --- a/packages/server/services/posts/package.json +++ b/packages/server/services/posts/package.json @@ -1,12 +1,6 @@ { "name": "posts", "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "proxy": { - "namespace": "/posts", - "port": 3001 - }, "dependencies": { "moment-timezone": "^0.5.45" } diff --git a/packages/server/services/posts/posts.service.js b/packages/server/services/posts/posts.service.js index ffe79cb8..7db00306 100644 --- a/packages/server/services/posts/posts.service.js +++ b/packages/server/services/posts/posts.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" import RedisClient from "@shared-classes/RedisClient" diff --git a/packages/server/services/sync/classes/VRCApi/index.js b/packages/server/services/sync/classes/VRCApi/index.js deleted file mode 100755 index 6e66ad30..00000000 --- a/packages/server/services/sync/classes/VRCApi/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import axios from "axios" - -export default class VRCApi { - static base_api_hostname = "https://api.vrchat.cloud/api/1" - - static get interface() { - return axios.create({ - baseURL: VRCApi.base_api_hostname, - headers: { - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36", - } - }) - } - - static async auth({ username, password }, access_token) { - username = encodeURIComponent(username) - password = encodeURIComponent(password) - - let headers = { - "Authorization": `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}` - } - - if (access_token) { - delete headers["Authorization"] - headers["Cookie"] = `${access_token}` - } - - const response = await VRCApi.interface({ - method: "GET", - url: "/auth/user", - headers, - }) - - if (!access_token && response.headers["set-cookie"]) { - response.data.cookie = response.headers["set-cookie"][0].split(";")[0] - } - - return response.data - } - - static async verifyOtp({ type, code }, access_token) { - const response = await VRCApi.interface({ - method: "POST", - url: `/auth/twofactorauth/${type}/verify`, - data: { - code: code - }, - headers: { - "Cookie": `${access_token}` - }, - }) - - if (!access_token && response.headers["set-cookie"]) { - response.data.cookie = response.headers["set-cookie"][0].split(";")[0] - } - - return response.data - } -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/main/index.js b/packages/server/services/sync/controllers/main/index.js deleted file mode 100755 index 6ce9343e..00000000 --- a/packages/server/services/sync/controllers/main/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import path from "path" -import createRoutesFromDirectory from "@utils/createRoutesFromDirectory" - -export default async (router) => { - router = createRoutesFromDirectory("routes", path.resolve(__dirname, "routes"), router) - - return { - path: "/", - router, - } -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/main/routes/get/active_services.js b/packages/server/services/sync/controllers/main/routes/get/active_services.js deleted file mode 100755 index 6c65a9f3..00000000 --- a/packages/server/services/sync/controllers/main/routes/get/active_services.js +++ /dev/null @@ -1,32 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError } from "@shared-classes/Errors" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - let access = {} - - const check_map = [ - ["vrc", async () => await SecureSyncEntry.has(req.session.user_id.toString(), "vrc_access_token")], - ["tidal", async () => await SecureSyncEntry.has(req.session.user_id.toString(), "tidal_access_token")], - ["spotify", async () => await SecureSyncEntry.has(req.session.user_id.toString(), "spotify_access_token")], - ] - - try { - for (const check of check_map) { - const [service, fn] = check - - if (await fn()) { - access[service] = true - } else { - access[service] = false - } - } - } catch (error) { - return res.status(500).json({ error: error.message }) - } - - return res.json(access) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/index.js b/packages/server/services/sync/controllers/services/index.js deleted file mode 100755 index e30b2052..00000000 --- a/packages/server/services/sync/controllers/services/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import path from "path" -import createRoutesFromDirectory from "@utils/createRoutesFromDirectory" - -export default async (router) => { - router = createRoutesFromDirectory("routes", path.resolve(__dirname, "routes"), router) - - return { - path: "/services", - router, - } -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/delete/tidal/track/[track_id]/like.js b/packages/server/services/sync/controllers/services/routes/delete/tidal/track/[track_id]/like.js deleted file mode 100755 index 1e33eb80..00000000 --- a/packages/server/services/sync/controllers/services/routes/delete/tidal/track/[track_id]/like.js +++ /dev/null @@ -1,34 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - let response = await TidalAPI.toggleTrackLike({ - trackId: req.params.track_id, - to: false, - user_id: user_data.id, - access_token, - country: user_data.country, - }) - - return res.json(response) - } catch (error) { - return new InternalServerError(req, res, error) - } -} diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/create_link.js b/packages/server/services/sync/controllers/services/routes/get/tidal/create_link.js deleted file mode 100755 index b5de570a..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/create_link.js +++ /dev/null @@ -1,47 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - const authProcess = await TidalAPI.getAuthUrl() - - if (!authProcess) { - return new InternalServerError(req, res) - } - - const checkInterval = setInterval(async () => { - const response = await TidalAPI.checkAuthStatus(authProcess.device_code).catch(() => { - return false - }) - - if (response) { - const userData = { - id: response.user.userId, - email: response.user.email, - username: response.user.username, - countryCode: response.user.countryCode, - } - - // save to SecureSyncEntry - await SecureSyncEntry.set(req.session.user_id.toString(), "tidal_user", JSON.stringify(userData)) - await SecureSyncEntry.set(req.session.user_id.toString(), "tidal_access_token", response.access_token) - await SecureSyncEntry.set(req.session.user_id.toString(), "tidal_refresh_token", response.refresh_token) - - return clearInterval(checkInterval) - } - }, 3000) - - setTimeout(() => { - clearInterval(checkInterval) - }, authProcess.expires_in * 1000) - - return res.json({ - auth_url: authProcess.url, - device_code: authProcess.deviceCode, - }) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/current.js b/packages/server/services/sync/controllers/services/routes/get/tidal/current.js deleted file mode 100755 index 7143396d..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/current.js +++ /dev/null @@ -1,22 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - let user = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - try { - user = JSON.parse(user) - - if (!user) { - return new NotFoundError(req, res) - } - - return res.json(user) - } catch (error) { - return new InternalServerError(req, res) - } -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/favorites/playlists.js b/packages/server/services/sync/controllers/services/routes/get/tidal/favorites/playlists.js deleted file mode 100755 index 60c924bb..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/favorites/playlists.js +++ /dev/null @@ -1,34 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - let response = await TidalAPI.getFavoritePlaylists({ - user_id: user_data.id, - country: user_data.countryCode, - access_token: access_token, - limit: Number(req.query.limit ?? 50), - offset: Number(req.query.offset ?? 0), - }) - - return res.json(response) - } catch (error) { - return new InternalServerError(req, res, error) - } -} diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/favorites/tracks.js b/packages/server/services/sync/controllers/services/routes/get/tidal/favorites/tracks.js deleted file mode 100755 index b1925878..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/favorites/tracks.js +++ /dev/null @@ -1,34 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - const response = await TidalAPI.getFavoriteTracks({ - user_id: user_data.id, - country: user_data.countryCode, - access_token: access_token, - limit: Number(req.query.limit ?? 50), - offset: Number(req.query.offset ?? 0), - }) - - return res.json(response) - } catch (error) { - return new InternalServerError(req, res, error) - } -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/is_active.js b/packages/server/services/sync/controllers/services/routes/get/tidal/is_active.js deleted file mode 100755 index 13407d79..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/is_active.js +++ /dev/null @@ -1,18 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, NotFoundError } from "@shared-classes/Errors" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - let hasUser = await SecureSyncEntry.has(req.session.user_id.toString(), "tidal_user") - - if (!hasUser) { - return new NotFoundError(req, res, "This account is not linked to a TIDAL account.") - } - - return res.json({ - active: hasUser - }) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/manifest/[track_id].js b/packages/server/services/sync/controllers/services/routes/get/tidal/manifest/[track_id].js deleted file mode 100755 index 9756c003..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/manifest/[track_id].js +++ /dev/null @@ -1,36 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - const response = await TidalAPI.getTrackManifest({ - track_id: req.params.track_id, - access_token: access_token, - country: user_data.countryCode - }) - - if (!response) { - return new NotFoundError(req, res, "Track is not available") - } - - return res.json(response) - } catch (error) { - return new InternalServerError(req, res, error) - } -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/playback/[track_id].js b/packages/server/services/sync/controllers/services/routes/get/tidal/playback/[track_id].js deleted file mode 100755 index e801ebec..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/playback/[track_id].js +++ /dev/null @@ -1,27 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - const response = await TidalAPI.getTrackPlaybackUrl({ - track_id: req.params.track_id, - access_token: access_token - }) - - if (!response) { - return new NotFoundError(req, res, "Track is not available") - } - - return res.json(response) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/data.js b/packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/data.js deleted file mode 100755 index 01612465..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/data.js +++ /dev/null @@ -1,35 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - let response = await TidalAPI.getPlaylistData({ - uuid: req.params.uuid, - country: user_data.countryCode, - access_token: access_token, - limit: Number(req.query.limit ?? 50), - offset: Number(req.query.offset ?? 0), - resolve_items: req.query.resolve_items === "true", - }) - - return res.json(response) - } catch (error) { - return new InternalServerError(req, res, error) - } -} diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/items.js b/packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/items.js deleted file mode 100755 index 61a90e47..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/playlist/[uuid]/items.js +++ /dev/null @@ -1,34 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError, NotFoundError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - let response = await TidalAPI.getPlaylistItems({ - uuid: req.params.uuid, - country: user_data.countryCode, - access_token: access_token, - limit: Number(req.query.limit ?? 50), - offset: Number(req.query.offset ?? 0), - }) - - return res.json(response) - } catch (error) { - return new InternalServerError(req, res, error) - } -} diff --git a/packages/server/services/sync/controllers/services/routes/get/tidal/search.js b/packages/server/services/sync/controllers/services/routes/get/tidal/search.js deleted file mode 100755 index 3dd115a4..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/tidal/search.js +++ /dev/null @@ -1,11 +0,0 @@ -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - const query = req.query - - const response = await TidalAPI.search({ - query: query.query - }) - - return res.json(response) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/get/vrc/session.js b/packages/server/services/sync/controllers/services/routes/get/vrc/session.js deleted file mode 100755 index feb561e3..00000000 --- a/packages/server/services/sync/controllers/services/routes/get/vrc/session.js +++ /dev/null @@ -1,12 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError } from "@shared-classes/Errors" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - const userData = await SecureSyncEntry.get(req.session.user_id.toString(), "vrc:user_data") - - return res.json(JSON.parse(userData)) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/post/tidal/delete_link.js b/packages/server/services/sync/controllers/services/routes/post/tidal/delete_link.js deleted file mode 100755 index 6b62f093..00000000 --- a/packages/server/services/sync/controllers/services/routes/post/tidal/delete_link.js +++ /dev/null @@ -1,16 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError } from "@shared-classes/Errors" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - await SecureSyncEntry.delete(req.session.user_id.toString(), "tidal_user") - await SecureSyncEntry.delete(req.session.user_id.toString(), "tidal_access_token") - await SecureSyncEntry.delete(req.session.user_id.toString(), "tidal_refresh_token") - - return res.json({ - success: true - }) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/post/tidal/track/[track_id]/like.js b/packages/server/services/sync/controllers/services/routes/post/tidal/track/[track_id]/like.js deleted file mode 100755 index 7993508a..00000000 --- a/packages/server/services/sync/controllers/services/routes/post/tidal/track/[track_id]/like.js +++ /dev/null @@ -1,35 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError, InternalServerError } from "@shared-classes/Errors" - -import TidalAPI from "@shared-classes/TidalAPI" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - try { - const access_token = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_access_token") - - if (!access_token) { - return new AuthorizationError(req, res, "Its needed to link your TIDAL account to perform this action.") - } - - let user_data = await SecureSyncEntry.get(req.session.user_id.toString(), "tidal_user") - - user_data = JSON.parse(user_data) - - let response = await TidalAPI.toggleTrackLike({ - trackId: req.params.track_id, - to: true, - user_id: user_data.id, - access_token, - country: user_data.countryCode, - }) - - return res.json(response) - } catch (error) { - console.error(error) - return new InternalServerError(req, res, error) - } -} diff --git a/packages/server/services/sync/controllers/services/routes/post/vrc/auth.js b/packages/server/services/sync/controllers/services/routes/post/vrc/auth.js deleted file mode 100755 index 1a1f1a8b..00000000 --- a/packages/server/services/sync/controllers/services/routes/post/vrc/auth.js +++ /dev/null @@ -1,57 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError } from "@shared-classes/Errors" - -import ExternalAPI from "@classes/VRCApi" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - req.body = await req.json() - - if (!req.body.username || !req.body.password) { - return res.status(400).json({ - success: false, - error: "Missing username or password", - }) - } - - const currentAccessToken = await SecureSyncEntry.get(req.session.user_id.toString(), "vrc:access_token") - - const result = await ExternalAPI.auth({ - username: req.body.username, - password: req.body.password - }, currentAccessToken).catch((error) => { - if (error.response) { - console.error(error.response.data) - - res.status(500).json(error.response.data.error) - } else { - res.status(500).json({ - error: "Something went wrong", - }) - } - - return null - }) - - if (!result) { - return false - } - - console.log(result) - - if (result.cookie) { - await SecureSyncEntry.set(req.session.user_id.toString(), "vrc:access_token", cookie) - } - - if (!result.requiresTwoFactorAuth) { - await SecureSyncEntry.set(req.session.user_id.toString(), "vrc:user_data", JSON.stringify(result)) - } - - return res.json({ - success: result.requiresTwoFactorAuth ? true : false, - ...result - }) -} \ No newline at end of file diff --git a/packages/server/services/sync/controllers/services/routes/post/vrc/logout.js b/packages/server/services/sync/controllers/services/routes/post/vrc/logout.js deleted file mode 100755 index e69de29b..00000000 diff --git a/packages/server/services/sync/controllers/services/routes/post/vrc/otp.js b/packages/server/services/sync/controllers/services/routes/post/vrc/otp.js deleted file mode 100755 index e9e24c82..00000000 --- a/packages/server/services/sync/controllers/services/routes/post/vrc/otp.js +++ /dev/null @@ -1,45 +0,0 @@ -import SecureSyncEntry from "@shared-classes/SecureSyncEntry" -import { AuthorizationError } from "@shared-classes/Errors" - -import ExternalAPI from "@classes/VRCApi" - -export default async (req, res) => { - if (!req.session) { - return new AuthorizationError(req, res) - } - - req.body = await req.json() - - const { type, code } = req.body - - if (!type || !code) { - return res.status(400).json({ - success: false, - error: "Missing type or code", - }) - } - - const authcookie = await SecureSyncEntry.get(req.session.user_id.toString(), "vrc:access_token") - - const result = await ExternalAPI.verifyOtp({ type, code }, authcookie).catch((error) => { - if (error.response) { - console.error(error.response.data) - - res.status(500).json(error.response.data.error) - } else { - console.error(error) - - res.status(500).json({ - error: "Something went wrong", - }) - } - - return null - }) - - if (!result) { - return false - } - - return res.json(result) -} \ No newline at end of file diff --git a/packages/server/services/sync/middlewares/withAuth/index.js b/packages/server/services/sync/middlewares/withAuth/index.js deleted file mode 100755 index 52a30f4a..00000000 --- a/packages/server/services/sync/middlewares/withAuth/index.js +++ /dev/null @@ -1,25 +0,0 @@ -export default async function (req, res, next) { - // extract authentification header - let auth = req.headers.authorization - - if (!auth) { - return res.status(401).json({ error: "Unauthorized, missing token" }) - } - - auth = auth.replace("Bearer ", "") - - // check if authentification is valid - const validation = await comty.rest.session.validateToken(auth).catch((error) => { - return { - valid: false, - } - }) - - if (!validation.valid) { - return res.status(401).json({ error: "Unauthorized" }) - } - - req.session = validation.data - - return next() -} \ No newline at end of file diff --git a/packages/server/services/sync/middlewares/withOptionalAuth/index.js b/packages/server/services/sync/middlewares/withOptionalAuth/index.js deleted file mode 100755 index 9689973d..00000000 --- a/packages/server/services/sync/middlewares/withOptionalAuth/index.js +++ /dev/null @@ -1,26 +0,0 @@ -export default async function (req, res, next) { - // extract authentification header - let auth = req.headers.authorization - - if (!auth) { - return next() - } - - auth = auth.replace("Bearer ", "") - - // check if authentification is valid - const validation = await comty.rest.session.validateToken(auth).catch((error) => { - return { - valid: false, - } - }) - - if (!validation.valid) { - return next() - } - - req.sessionToken = auth - req.session = validation.data - - return next() -} \ No newline at end of file diff --git a/packages/server/services/sync/middlewares/withWsAuth.js b/packages/server/services/sync/middlewares/withWsAuth.js deleted file mode 100755 index 31fed9d9..00000000 --- a/packages/server/services/sync/middlewares/withWsAuth.js +++ /dev/null @@ -1,55 +0,0 @@ -export default async (socket, next) => { - try { - const token = socket.handshake.auth.token - - if (!token) { - return next(new Error(`auth:token_missing`)) - } - - const validation = await global.comty.rest.session.validateToken(token).catch((err) => { - console.error(`[${socket.id}] failed to validate session caused by server error`, err) - - return { - valid: false, - error: err, - } - }) - - if (!validation.valid) { - if (validation.error) { - return next(new Error(`auth:server_error`)) - } - - return next(new Error(`auth:token_invalid`)) - } - - const session = validation.data - - const userData = await global.comty.rest.user.data({ - user_id: session.user_id, - }).catch((err) => { - console.error(`[${socket.id}] failed to get user data caused by server error`, err) - - return null - }) - - if (!userData) { - return next(new Error(`auth:user_failed`)) - } - - try { - socket.userData = userData - socket.token = token - socket.session = session - } - catch (err) { - return next(new Error(`auth:decode_failed`)) - } - - next() - } catch (error) { - console.error(`[${socket.id}] failed to connect caused by server error`, error) - - next(new Error(`auth:authentification_failed`)) - } -} \ No newline at end of file diff --git a/packages/server/services/sync/package.json b/packages/server/services/sync/package.json deleted file mode 100755 index a0d8962a..00000000 --- a/packages/server/services/sync/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "sync", - "version": "0.60.2", - "license": "MIT", - "dependencies": { - "@foxify/events": "^2.1.0", - "axios": "^1.2.5", - "bcrypt": "^5.1.0", - "comty.js": "^0.58.2", - "connect-mongo": "^4.6.0", - "content-range": "^2.0.2", - "dotenv": "^16.0.3", - "file-api": "^0.10.4", - "form-data": "^4.0.0", - "hyper-express": "^6.5.9", - "jsonwebtoken": "^9.0.0", - "linebridge": "0.15.12", - "luxon": "^3.2.1", - "normalize-url": "^8.0.0", - "p-map": "^6.0.0", - "p-queue": "^7.3.4", - "qs": "^6.11.2", - "redis": "^4.6.6" - } -} \ No newline at end of file diff --git a/packages/server/services/sync/sync.service.js b/packages/server/services/sync/sync.service.js deleted file mode 100755 index c1facbdc..00000000 --- a/packages/server/services/sync/sync.service.js +++ /dev/null @@ -1,144 +0,0 @@ -import fs from "fs" -import path from "path" - -import DbManager from "@shared-classes/DbManager" -import RedisClient from "@shared-classes/RedisClient" -import ComtyClient from "@shared-classes/ComtyClient" - -import hyperexpress from "hyper-express" - -import pkg from "./package.json" - -export default class API { - static useMiddlewaresOrder = ["useLogger", "useCors", "useAuth"] - - server = global.server = new hyperexpress.Server() - - listenIp = process.env.HTTP_LISTEN_IP ?? "0.0.0.0" - listenPort = process.env.HTTP_LISTEN_PORT ?? 3006 - - internalRouter = new hyperexpress.Router() - - db = new DbManager() - - ssePools = global.ssePools = {} - - comty = global.comty = ComtyClient({ - useWs: false, - }) - - redis = global.redis = RedisClient() - - async __registerControllers() { - let controllersPath = fs.readdirSync(path.resolve(__dirname, "controllers")) - - for await (const controllerPath of controllersPath) { - const controller = require(path.resolve(__dirname, "controllers", controllerPath)).default - - if (!controller) { - console.error(`Controller ${controllerPath} not found.`) - - continue - } - - const controllerRouter = new hyperexpress.Router() - - const controllerOutput = await controller(controllerRouter) - - if (!controllerOutput) { - console.error(`Controller ${controllerPath} returning not valid handler.`) - - continue - } - - this.internalRouter.use(controllerOutput.path ?? "/", controllerOutput.router) - - continue - } - } - - async __registerInternalMiddlewares() { - let middlewaresPath = fs.readdirSync(path.resolve(__dirname, "useMiddlewares")) - - // sort middlewares - middlewaresPath = middlewaresPath.sort((a, b) => { - const aIndex = this.constructor.useMiddlewaresOrder.indexOf(a.replace(".js", "")) - const bIndex = this.constructor.useMiddlewaresOrder.indexOf(b.replace(".js", "")) - - if (aIndex === -1) { - return 1 - } - - if (bIndex === -1) { - return -1 - } - - return aIndex - bIndex - }) - - for await (const middlewarePath of middlewaresPath) { - const middleware = require(path.resolve(__dirname, "useMiddlewares", middlewarePath)).default - - if (!middleware) { - console.error(`Middleware ${middlewarePath} not found.`) - - continue - } - - this.server.use(middleware) - } - } - - __registerInternalRoutes() { - this.server.get("/", (req, res) => { - return res.status(200).json({ - name: pkg.name, - version: pkg.version, - routes: this.__getRegisteredRoutes() - }) - }) - - this.server.any("*", (req, res) => { - return res.status(404).json({ - error: "Not found", - }) - }) - } - - __getRegisteredRoutes() { - return this.internalRouter.routes.map((route) => { - return { - method: route.method, - path: route.pattern, - } - }) - } - - async initialize() { - const startHrTime = process.hrtime() - - // initialize clients - await this.db.initialize() - await this.redis.initialize() - - // register controllers & middlewares - await this.__registerInternalRoutes() - await this.__registerControllers() - await this.__registerInternalMiddlewares() - - // use internal router - this.server.use(this.internalRouter) - - // start server - await this.server.listen(this.listenPort, this.listenIp) - - // calculate elapsed time - const elapsedHrTime = process.hrtime(startHrTime) - const elapsedTimeInMs = elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6 - - // log server started - console.log(`🚀 Server started ready on \n\t - http://${this.listenIp}:${this.listenPort} \n\t - Tooks ${elapsedTimeInMs}ms`) - } -} - -Boot(API) \ No newline at end of file diff --git a/packages/server/services/sync/useMiddlewares/useAuth/index.js b/packages/server/services/sync/useMiddlewares/useAuth/index.js deleted file mode 100755 index 4d5bece4..00000000 --- a/packages/server/services/sync/useMiddlewares/useAuth/index.js +++ /dev/null @@ -1,25 +0,0 @@ -export default async function (req, res, next) { - // extract authentification header - let auth = req.headers.authorization - - if (!auth) { - return false - } - - auth = auth.replace("Bearer ", "") - - // check if authentification is valid - const validation = await comty.rest.session.validateToken(auth).catch((error) => { - return { - valid: false, - } - }) - - if (!validation.valid) { - return res.status(401).json({ error: "Unauthorized" }) - } - - req.session = validation.data - - return true -} \ No newline at end of file diff --git a/packages/server/services/sync/useMiddlewares/useCors/index.js b/packages/server/services/sync/useMiddlewares/useCors/index.js deleted file mode 100755 index 7ebac0fc..00000000 --- a/packages/server/services/sync/useMiddlewares/useCors/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import cors from "cors" - -export default cors({ - origin: "*", - methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT", "TRACE"], - preflightContinue: false, - optionsSuccessStatus: 204, -}) \ No newline at end of file diff --git a/packages/server/services/sync/useMiddlewares/useLogger/index.js b/packages/server/services/sync/useMiddlewares/useLogger/index.js deleted file mode 100755 index 5e9c64f4..00000000 --- a/packages/server/services/sync/useMiddlewares/useLogger/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export default (req, res, next) => { - const startHrTime = process.hrtime() - - res.on("finish", () => { - const elapsedHrTime = process.hrtime(startHrTime) - const elapsedTimeInMs = elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6 - - res._responseTimeMs = elapsedTimeInMs - - console.log(`${req.method} ${res._status_code ?? res.statusCode ?? 200} ${req.url} ${elapsedTimeInMs}ms`) - }) - - next() -} \ No newline at end of file diff --git a/packages/server/services/sync/utils/composePayloadData/index.js b/packages/server/services/sync/utils/composePayloadData/index.js deleted file mode 100755 index de5761f5..00000000 --- a/packages/server/services/sync/utils/composePayloadData/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export default function composePayloadData(socket, data = {}) { - return { - user: { - user_id: socket.userData._id, - username: socket.userData.username, - fullName: socket.userData.fullName, - avatar: socket.userData.avatar, - }, - command_issuer: data.command_issuer ?? socket.userData._id, - ...data - } -} \ No newline at end of file diff --git a/packages/server/services/sync/utils/createRoutesFromDirectory/index.js b/packages/server/services/sync/utils/createRoutesFromDirectory/index.js deleted file mode 100755 index f27647a0..00000000 --- a/packages/server/services/sync/utils/createRoutesFromDirectory/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import fs from "fs" - -function createRoutesFromDirectory(startFrom, directoryPath, router) { - const files = fs.readdirSync(directoryPath) - - files.forEach((file) => { - const filePath = `${directoryPath}/${file}` - - const stat = fs.statSync(filePath) - - if (stat.isDirectory()) { - createRoutesFromDirectory(startFrom, filePath, router) - } else if (file.endsWith(".js") || file.endsWith(".jsx") || file.endsWith(".ts") || file.endsWith(".tsx")) { - let splitedFilePath = filePath.split("/") - - // slice the startFrom path - splitedFilePath = splitedFilePath.slice(splitedFilePath.indexOf(startFrom) + 1) - - const method = splitedFilePath[0] - - let route = splitedFilePath.slice(1, splitedFilePath.length).join("/") - - route = route.replace(".jsx", "") - route = route.replace(".js", "") - route = route.replace(".ts", "") - route = route.replace(".tsx", "") - - if (route === "index") { - route = "/" - } else { - route = `/${route}` - } - - let handler = require(filePath) - - handler = handler.default || handler - - router[method](route, handler) - } - }) - - return router -} - -export default createRoutesFromDirectory \ No newline at end of file diff --git a/packages/server/services/sync/utils/generateFnHandler/index.js b/packages/server/services/sync/utils/generateFnHandler/index.js deleted file mode 100755 index 4c5962d5..00000000 --- a/packages/server/services/sync/utils/generateFnHandler/index.js +++ /dev/null @@ -1,21 +0,0 @@ -export default function generateFnHandler(fn, socket) { - return async (...args) => { - if (typeof socket === "undefined") { - socket = arguments[0] - } - - try { - fn(socket, ...args) - } catch (error) { - console.error(`[HANDLER_ERROR] ${error.message} >`, error.stack) - - if (typeof socket.emit !== "function") { - return false - } - - return socket.emit("error", { - message: error.message, - }) - } - } -} \ No newline at end of file diff --git a/packages/server/services/sync/utils/getMiddlewares/index.js b/packages/server/services/sync/utils/getMiddlewares/index.js deleted file mode 100755 index 78a65ea0..00000000 --- a/packages/server/services/sync/utils/getMiddlewares/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import fs from "node:fs" -import path from "node:path" - -export default async (middlewares, middlewaresPath) => { - if (typeof middlewaresPath === "undefined") { - middlewaresPath = path.resolve(globalThis["__src"], "middlewares") - } - - if (!fs.existsSync(middlewaresPath)) { - return undefined - } - - if (typeof middlewares === "string") { - middlewares = [middlewares] - } - - let fns = [] - - for await (const middlewareName of middlewares) { - const middlewarePath = path.resolve(middlewaresPath, middlewareName) - - if (!fs.existsSync(middlewarePath)) { - console.error(`Middleware ${middlewareName} not found.`) - - continue - } - - const middleware = require(middlewarePath).default - - if (!middleware) { - console.error(`Middleware ${middlewareName} not valid export.`) - - continue - } - - if (typeof middleware !== "function") { - console.error(`Middleware ${middlewareName} not valid function.`) - - continue - } - - fns.push(middleware) - } - - return fns -} \ No newline at end of file diff --git a/packages/server/services/sync/utils/resolveUrl/index.js b/packages/server/services/sync/utils/resolveUrl/index.js deleted file mode 100755 index a9a33785..00000000 --- a/packages/server/services/sync/utils/resolveUrl/index.js +++ /dev/null @@ -1,20 +0,0 @@ -export default (from, to) => { - const resolvedUrl = new URL(to, new URL(from, "resolve://")) - - if (resolvedUrl.protocol === "resolve:") { - let { pathname, search, hash } = resolvedUrl - - if (to.includes("@")) { - const fromUrl = new URL(from) - const toUrl = new URL(to, fromUrl.origin) - - pathname = toUrl.pathname - search = toUrl.search - hash = toUrl.hash - } - - return pathname + search + hash - } - - return resolvedUrl.toString() -} \ No newline at end of file diff --git a/packages/server/services/users/classes/users/method/getFollowers.js b/packages/server/services/users/classes/users/method/getFollowers.js index 663284bd..361e5574 100644 --- a/packages/server/services/users/classes/users/method/getFollowers.js +++ b/packages/server/services/users/classes/users/method/getFollowers.js @@ -24,7 +24,7 @@ export default async (payload = {}) => { return followersData } else { - const count = await UserFollow.count({ + const count = await UserFollow.countDocuments({ to: user_id }) diff --git a/packages/server/services/users/classes/users/method/toggleFollow.js b/packages/server/services/users/classes/users/method/toggleFollow.js index dbd36af9..5d916d4a 100644 --- a/packages/server/services/users/classes/users/method/toggleFollow.js +++ b/packages/server/services/users/classes/users/method/toggleFollow.js @@ -46,6 +46,6 @@ export default async (payload = {}) => { return { following: to, - count: await UserFollow.count({ to: user_id }), + count: await UserFollow.countDocuments({ to: user_id }), } } \ No newline at end of file diff --git a/packages/server/services/users/package.json b/packages/server/services/users/package.json index 41a56905..52e359d7 100644 --- a/packages/server/services/users/package.json +++ b/packages/server/services/users/package.json @@ -1,6 +1,4 @@ { "name": "users", - "version": "1.0.0", - "main": "index.js", - "license": "MIT" + "version": "1.0.0" } diff --git a/packages/server/services/users/users.service.js b/packages/server/services/users/users.service.js index f2a433e2..851448de 100644 --- a/packages/server/services/users/users.service.js +++ b/packages/server/services/users/users.service.js @@ -1,4 +1,4 @@ -import { Server } from "linebridge/src/server" +import { Server } from "linebridge/dist/server" import DbManager from "@shared-classes/DbManager" import RedisClient from "@shared-classes/RedisClient"