diff --git a/.vscode/launch.json b/.vscode/launch.json index 057362d..c2f73bb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -94,31 +94,31 @@ "name": "Next.js: debug client-side (app)", "type": "chrome", "request": "launch", - "url": "http://localhost:3000" + "url": "http://localhost:5000" }, { "name": "Next.js: debug client-side (web)", "type": "chrome", "request": "launch", - "url": "http://localhost:3001" + "url": "http://localhost:5001" }, { "name": "Next.js: debug client-side (api)", "type": "chrome", "request": "launch", - "url": "http://localhost:3002" + "url": "http://localhost:5002" }, { "name": "Next.js: debug client-side (email)", "type": "chrome", "request": "launch", - "url": "http://localhost:3003" + "url": "http://localhost:5003" }, { "name": "Next.js: debug client-side (app)", "type": "chrome", "request": "launch", - "url": "http://localhost:3004" + "url": "http://localhost:5004" }, { "name": "Next.js: debug full stack", diff --git a/Cargo.lock b/Cargo.lock index 829e444..590de9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,6 +246,15 @@ dependencies = [ "quick-xml 0.37.1", ] +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1362,6 +1371,30 @@ dependencies = [ "syn 2.0.92", ] +[[package]] +name = "dlsignal" +version = "0.1.0" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "eyre", + "futures", + "itertools 0.13.0", + "lazy_static", + "librqbit-core", + "qbit-rs", + "quirks_path", + "regex", + "reqwest", + "serde", + "testcontainers", + "testcontainers-modules", + "thiserror 2.0.9", + "tokio", + "url", +] + [[package]] name = "docker_credential" version = "1.3.1" @@ -1536,6 +1569,22 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "pear", + "serde", + "serde_json", + "serde_yaml", + "toml", + "uncased", + "version_check", +] + [[package]] name = "filetime" version = "0.2.25" @@ -2431,6 +2480,12 @@ dependencies = [ "syn 2.0.92", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "insta" version = "1.41.1" @@ -3636,6 +3691,29 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.92", +] + [[package]] name = "pem" version = "3.0.4" @@ -4180,8 +4258,10 @@ dependencies = [ "axum-auth", "bytes", "chrono", + "dlsignal", "eyre", "fancy-regex", + "figment", "html-escape", "insta", "itertools 0.13.0", @@ -4209,7 +4289,6 @@ dependencies = [ "serial_test", "thiserror 2.0.9", "tokio", - "torrent", "tracing", "tracing-subscriber", "url", @@ -6125,30 +6204,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "torrent" -version = "0.1.0" -dependencies = [ - "async-trait", - "bytes", - "chrono", - "eyre", - "futures", - "itertools 0.13.0", - "lazy_static", - "librqbit-core", - "qbit-rs", - "quirks_path", - "regex", - "reqwest", - "serde", - "testcontainers", - "testcontainers-modules", - "thiserror 2.0.9", - "tokio", - "url", -] - [[package]] name = "tower" version = "0.4.13" @@ -6376,6 +6431,15 @@ dependencies = [ "web-time", ] +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unic-char-property" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 429c0db..9492ba0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["apps/recorder", "packages/quirks-path", "packages/torrent"] +members = ["apps/recorder", "packages/quirks-path", "packages/dlsignal"] resolver = "2" diff --git a/apps/api/.env.development b/apps/api/.env.development deleted file mode 100644 index a5eebbc..0000000 --- a/apps/api/.env.development +++ /dev/null @@ -1,14 +0,0 @@ -# Server -DATABASE_URL="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu" -BETTERSTACK_API_KEY="" -BETTERSTACK_URL="" -FLAGS_SECRET="" -ARCJET_KEY="" -SVIX_TOKEN="" -LIVEBLOCKS_SECRET="" - -# Client -NEXT_PUBLIC_APP_URL="http://localhost:3000" -NEXT_PUBLIC_WEB_URL="http://localhost:3001" -NEXT_PUBLIC_DOCS_URL="http://localhost:3004" -NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://webui.konobangu.com" \ No newline at end of file diff --git a/apps/api/.env.example b/apps/api/.env.example deleted file mode 100644 index ca7d1ee..0000000 --- a/apps/api/.env.example +++ /dev/null @@ -1,15 +0,0 @@ -# Server -BETTER_AUTH_SECRET="" -DATABASE_URL="" -BETTERSTACK_API_KEY="" -BETTERSTACK_URL="" -FLAGS_SECRET="" -ARCJET_KEY="" -SVIX_TOKEN="" -LIVEBLOCKS_SECRET="" - -# Client -NEXT_PUBLIC_APP_URL="http://localhost:3000" -NEXT_PUBLIC_WEB_URL="http://localhost:3001" -NEXT_PUBLIC_DOCS_URL="http://localhost:3004" -NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="http://localhost:3000" \ No newline at end of file diff --git a/apps/api/.gitignore b/apps/api/.gitignore deleted file mode 100644 index 663a9cc..0000000 --- a/apps/api/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts - -# prisma -.env - -# react.email -.react-email - -# Sentry -.sentryclirc \ No newline at end of file diff --git a/apps/api/app/apple-icon.png b/apps/api/app/apple-icon.png deleted file mode 100644 index d185929..0000000 Binary files a/apps/api/app/apple-icon.png and /dev/null differ diff --git a/apps/api/app/global-error.tsx b/apps/api/app/global-error.tsx deleted file mode 100644 index 6e0511e..0000000 --- a/apps/api/app/global-error.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client'; - -import { Button } from '@konobangu/design-system/components/ui/button'; -import { fonts } from '@konobangu/design-system/lib/fonts'; -import { captureException } from '@sentry/nextjs'; -import type NextError from 'next/error'; -import { useEffect } from 'react'; - -type GlobalErrorProperties = { - readonly error: NextError & { digest?: string }; - readonly reset: () => void; -}; - -const GlobalError = ({ error, reset }: GlobalErrorProperties) => { - useEffect(() => { - captureException(error); - }, [error]); - - return ( - - -

Oops, something went wrong

- - - - ); -}; - -export default GlobalError; diff --git a/apps/api/app/icon.png b/apps/api/app/icon.png deleted file mode 100644 index d798285..0000000 Binary files a/apps/api/app/icon.png and /dev/null differ diff --git a/apps/api/app/layout.tsx b/apps/api/app/layout.tsx deleted file mode 100644 index 23be9db..0000000 --- a/apps/api/app/layout.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { ReactNode } from 'react'; - -type RootLayoutProperties = { - readonly children: ReactNode; -}; - -const RootLayout = ({ children }: RootLayoutProperties) => ( - - {children} - -); - -export default RootLayout; diff --git a/apps/api/app/opengraph-image.png b/apps/api/app/opengraph-image.png deleted file mode 100644 index c79169f..0000000 Binary files a/apps/api/app/opengraph-image.png and /dev/null differ diff --git a/apps/api/instrumentation.ts b/apps/api/instrumentation.ts deleted file mode 100644 index 99d8478..0000000 --- a/apps/api/instrumentation.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { initializeSentry } from '@konobangu/next-config/instrumentation'; - -export const register = initializeSentry(); diff --git a/apps/api/next.config.ts b/apps/api/next.config.ts deleted file mode 100644 index 5ca2ed6..0000000 --- a/apps/api/next.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { env } from '@konobangu/env'; -import { config, withAnalyzer, withSentry } from '@konobangu/next-config'; -import type { NextConfig } from 'next'; - -let nextConfig: NextConfig = { ...config }; - -if (env.VERCEL) { - nextConfig = withSentry(nextConfig); -} - -if (env.ANALYZE === 'true') { - nextConfig = withAnalyzer(nextConfig); -} - -export default nextConfig; diff --git a/apps/api/package.json b/apps/api/package.json deleted file mode 100644 index b2dbcb7..0000000 --- a/apps/api/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "api", - "private": true, - "scripts": { - "dev": "concurrently \"pnpm:next\"", - "next": "next dev -p 3002 --turbopack", - "build": "next build", - "start": "next start", - "analyze": "ANALYZE=true pnpm build", - "clean": "git clean -xdf .cache .turbo dist node_modules", - "typecheck": "tsc --noEmit --emitDeclarationOnly false" - }, - "dependencies": { - "@konobangu/analytics": "workspace:*", - "@konobangu/auth": "workspace:*", - "@konobangu/database": "workspace:*", - "@konobangu/design-system": "workspace:*", - "@konobangu/env": "workspace:*", - "@konobangu/next-config": "workspace:*", - "@konobangu/observability": "workspace:*", - "@sentry/nextjs": "^8.43.0", - "import-in-the-middle": "^1.11.3", - "next": "^15.1.3", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "require-in-the-middle": "^7.4.0", - "svix": "^1.43.0" - }, - "devDependencies": { - "@konobangu/typescript-config": "workspace:*", - "@types/node": "22.10.1", - "@types/react": "19.0.1", - "@types/react-dom": "19.0.2", - "concurrently": "^9.1.0", - "typescript": "^5.7.2" - } -} diff --git a/apps/api/sentry.client.config.ts b/apps/api/sentry.client.config.ts deleted file mode 100644 index 9f17f95..0000000 --- a/apps/api/sentry.client.config.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file configures the initialization of Sentry on the client. - * The config you add here will be used whenever a users loads a page in their browser. - * https://docs.sentry.io/platforms/javascript/guides/nextjs/ - */ - -import { init, replayIntegration } from '@sentry/nextjs'; - -init({ - dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, - - // Adjust this value in production, or use tracesSampler for greater control - tracesSampleRate: 1, - - // Setting this option to true will print useful information to the console while you're setting up Sentry. - debug: false, - - replaysOnErrorSampleRate: 1, - - /* - * This sets the sample rate to be 10%. You may want this to be 100% while - * in development and sample at a lower rate in production - */ - replaysSessionSampleRate: 0.1, - - // You can remove this option if you're not planning to use the Sentry Session Replay feature: - integrations: [ - replayIntegration({ - // Additional Replay configuration goes in here, for example: - maskAllText: true, - blockAllMedia: true, - }), - ], -}); diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json deleted file mode 100644 index 2f5c616..0000000 --- a/apps/api/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "@konobangu/typescript-config/nextjs.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["./*"], - "@konobangu/*": ["../../packages/*"] - } - }, - "include": [ - "next-env.d.ts", - "next.config.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ] -} diff --git a/apps/app/.env.development b/apps/app/.env.development index e76c34d..9ced364 100644 --- a/apps/app/.env.development +++ b/apps/app/.env.development @@ -23,7 +23,7 @@ SVIX_TOKEN="" LIVEBLOCKS_SECRET="" # Client -NEXT_PUBLIC_APP_URL="http://localhost:3000" -NEXT_PUBLIC_WEB_URL="http://localhost:3001" -NEXT_PUBLIC_DOCS_URL="http://localhost:3004" -NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://webui.konobangu.com" \ No newline at end of file +NEXT_PUBLIC_APP_URL="http://localhost:5000" +NEXT_PUBLIC_WEB_URL="http://localhost:5001" +NEXT_PUBLIC_DOCS_URL="http://localhost:5004" +NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com" \ No newline at end of file diff --git a/apps/app/.env.example b/apps/app/.env.example index 905ce06..3144d50 100644 --- a/apps/app/.env.example +++ b/apps/app/.env.example @@ -23,7 +23,7 @@ SVIX_TOKEN="" LIVEBLOCKS_SECRET="" # WEBUI -NEXT_PUBLIC_APP_URL="http://localhost:3000" -NEXT_PUBLIC_WEB_URL="http://localhost:3001" -NEXT_PUBLIC_DOCS_URL="http://localhost:3004" +NEXT_PUBLIC_APP_URL="http://localhost:5000" +NEXT_PUBLIC_WEB_URL="http://localhost:5001" +NEXT_PUBLIC_DOCS_URL="http://localhost:5004" NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com" \ No newline at end of file diff --git a/apps/api/app/cron/keep-alive/route.ts b/apps/app/app/cron/keep-alive/route.ts similarity index 100% rename from apps/api/app/cron/keep-alive/route.ts rename to apps/app/app/cron/keep-alive/route.ts diff --git a/apps/api/app/health/route.ts b/apps/app/app/health/route.ts similarity index 100% rename from apps/api/app/health/route.ts rename to apps/app/app/health/route.ts diff --git a/apps/app/package.json b/apps/app/package.json index d50a4f4..8c5943e 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -2,7 +2,7 @@ "name": "app", "private": true, "scripts": { - "dev": "next dev -p 3000 --turbopack", + "dev": "next dev -p 5000 --turbopack", "build": "next build", "start": "next start", "analyze": "ANALYZE=true pnpm build", diff --git a/apps/api/vercel.json b/apps/app/vercel.json similarity index 100% rename from apps/api/vercel.json rename to apps/app/vercel.json diff --git a/apps/docs/development.mdx b/apps/docs/development.mdx index 8783008..0167dc4 100644 --- a/apps/docs/development.mdx +++ b/apps/docs/development.mdx @@ -28,11 +28,11 @@ Step 2. Go to the docs are located (where you can find `mint.json`) and run the mintlify dev ``` -The documentation website is now available at `http://localhost:3000`. +The documentation website is now available at `http://localhost:5000`. ### Custom Ports -Mintlify uses port 3000 by default. You can use the `--port` flag to customize the port Mintlify runs on. For example, use this command to run in port 3333: +Mintlify uses port 5000 by default. You can use the `--port` flag to customize the port Mintlify runs on. For example, use this command to run in port 3333: ```bash mintlify dev --port 3333 @@ -41,7 +41,7 @@ mintlify dev --port 3333 You will see an error like this if you try to run Mintlify in a port that's already taken: ```md -Error: listen EADDRINUSE: address already in use :::3000 +Error: listen EADDRINUSE: address already in use :::5000 ``` ## Mintlify Versions diff --git a/apps/docs/package.json b/apps/docs/package.json index d8b11e6..0489a8c 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -2,7 +2,7 @@ "name": "docs", "private": true, "scripts": { - "dev": "npx --yes mintlify dev --port 3004", + "dev": "npx --yes mintlify dev --port 5004", "lint": "npx --yes mintlify broken-links" }, "devDependencies": { diff --git a/apps/email-playground/package.json b/apps/email-playground/package.json index 7209526..ee05053 100644 --- a/apps/email-playground/package.json +++ b/apps/email-playground/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "build": "email build", - "dev": "email dev --port 3003", + "dev": "email dev --port 5003", "export": "email export", "clean": "git clean -xdf .cache .turbo dist node_modules", "typecheck": "tsc --noEmit --emitDeclarationOnly false" diff --git a/apps/proxy/.whistle/rules/files/0.webui b/apps/proxy/.whistle/rules/files/0.webui index 90e67cb..bca1aca 100644 --- a/apps/proxy/.whistle/rules/files/0.webui +++ b/apps/proxy/.whistle/rules/files/0.webui @@ -1,2 +1,2 @@ -^https://webui.konobangu.com/*** http://127.0.0.1:3000/$1 -^wss://webui.konobangu.com/*** ws://127.0.0.1:3000/$1 \ No newline at end of file +^https://konobangu.com/*** http://127.0.0.1:5000/$1 +^wss://konobangu.com/*** ws://127.0.0.1:5000/$1 \ No newline at end of file diff --git a/apps/recorder/.devcontainer/devcontainer.json b/apps/recorder/.devcontainer/devcontainer.json index 368a66e..f85ac5a 100644 --- a/apps/recorder/.devcontainer/devcontainer.json +++ b/apps/recorder/.devcontainer/devcontainer.json @@ -1,9 +1,7 @@ { - "name": "Konobangu Recorder", - "dockerComposeFile": "docker-compose.yml", - "service": "app", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", - "forwardPorts": [ - 3001 - ] -} \ No newline at end of file + "name": "Konobangu Recorder", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "forwardPorts": [5001] +} diff --git a/apps/recorder/Cargo.toml b/apps/recorder/Cargo.toml index 17a7d95..8336fa5 100644 --- a/apps/recorder/Cargo.toml +++ b/apps/recorder/Cargo.toml @@ -15,7 +15,7 @@ required-features = [] [dependencies] quirks_path = { path = "../../packages/quirks-path" } -torrent = { path = "../../packages/torrent" } +dlsignal = { path = "../../packages/dlsignal" } loco-rs = { version = "0.13" } serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -29,8 +29,9 @@ sea-orm = { version = "1", features = [ "sqlx-postgres", "runtime-tokio-rustls", "macros", - "debug-print" + "debug-print", ] } +figment = { version = "0.10", features = ["toml", "json", "env", "yaml"] } axum = "0.7.9" uuid = { version = "1.6.0", features = ["v4"] } diff --git a/apps/recorder/src/app.rs b/apps/recorder/src/app.rs index 821e5d5..88df8ba 100644 --- a/apps/recorder/src/app.rs +++ b/apps/recorder/src/app.rs @@ -34,7 +34,7 @@ pub trait AppContextExt { } fn get_auth_service(&self) -> &AppAuthService { - &AppAuthService::app_instance() + AppAuthService::app_instance() } } diff --git a/apps/recorder/src/auth/oidc.rs b/apps/recorder/src/auth/oidc.rs index 7e48c39..e1661ff 100644 --- a/apps/recorder/src/auth/oidc.rs +++ b/apps/recorder/src/auth/oidc.rs @@ -93,7 +93,7 @@ impl AuthService for OidcAuthService { let token_data = self.authorizer.check_auth(&token).await?; let claims = token_data.claims; - if !claims.sub.as_deref().is_some_and(|s| !s.trim().is_empty()) { + if claims.sub.as_deref().is_none_or(|s| s.trim().is_empty()) { return Err(AuthError::OidcSubMissingError); } if !claims.contains_audience(&config.audience) { @@ -103,7 +103,7 @@ impl AuthService for OidcAuthService { let found_scopes = claims.scopes().collect::>(); if !expected_scopes .iter() - .all(|es| found_scopes.contains(&es as &str)) + .all(|es| found_scopes.contains(es as &str)) { return Err(AuthError::OidcExtraScopesMatchError { expected: expected_scopes.iter().join(","), diff --git a/apps/recorder/src/auth/service.rs b/apps/recorder/src/auth/service.rs index a2395f1..2d61d9f 100644 --- a/apps/recorder/src/auth/service.rs +++ b/apps/recorder/src/auth/service.rs @@ -107,7 +107,7 @@ impl Initializer for AppAuthServiceInitializer { let service = AppAuthService::from_conf(auth_conf) .await - .map_err(|e| loco_rs::Error::wrap(e))?; + .map_err(loco_rs::Error::wrap)?; APP_AUTH_SERVICE.get_or_init(|| service); diff --git a/apps/recorder/src/config/mod.rs b/apps/recorder/src/config/mod.rs index cd3dbb3..08f8e72 100644 --- a/apps/recorder/src/config/mod.rs +++ b/apps/recorder/src/config/mod.rs @@ -1,12 +1,18 @@ +use figment::{ + providers::{Format, Json, Yaml}, + Figment, +}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{auth::AppAuthConfig, dal::config::AppDalConfig, extract::mikan::AppMikanConfig}; +const DEFAULT_APP_SETTINGS_MIXIN: &str = include_str!("./settings_mixin.yaml"); + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AppConfig { pub auth: AppAuthConfig, - pub dal: Option, - pub mikan: Option, + pub dal: AppDalConfig, + pub mikan: AppMikanConfig, } pub fn deserialize_key_path_from_json_value( @@ -42,10 +48,19 @@ pub trait AppConfigExt { fn get_root_conf(&self) -> &loco_rs::config::Config; fn get_app_conf(&self) -> loco_rs::Result { - Ok( - deserialize_key_path_from_app_config(self.get_root_conf(), &[])? - .expect("app config must be present"), - ) + let settings_str = self + .get_root_conf() + .settings + .as_ref() + .map(serde_json::to_string) + .unwrap_or_else(|| Ok(String::new()))?; + + let app_config = Figment::from(Json::string(&settings_str)) + .merge(Yaml::string(DEFAULT_APP_SETTINGS_MIXIN)) + .extract() + .map_err(loco_rs::Error::wrap)?; + + Ok(app_config) } } diff --git a/apps/recorder/src/config/settings_mixin.yaml b/apps/recorder/src/config/settings_mixin.yaml new file mode 100644 index 0000000..e294736 --- /dev/null +++ b/apps/recorder/src/config/settings_mixin.yaml @@ -0,0 +1,12 @@ +dal: + data_dir: ./data + +mikan: + http_client: + exponential_backoff_max_retries: 3 + leaky_bucket_max_tokens: 3 + leaky_bucket_initial_tokens: 0 + leaky_bucket_refill_tokens: 1 + leaky_bucket_refill_interval: 500 + user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0" + base_url: "https://mikanani.me/" diff --git a/apps/recorder/src/dal/client.rs b/apps/recorder/src/dal/client.rs index ecd27d6..4faabee 100644 --- a/apps/recorder/src/dal/client.rs +++ b/apps/recorder/src/dal/client.rs @@ -194,7 +194,7 @@ impl Initializer for AppDalInitalizer { let config = &app_context.config; let app_dal_conf = config.get_app_conf()?.dal; - APP_DAL_CLIENT.get_or_init(|| AppDalClient::new(app_dal_conf.unwrap_or_default())); + APP_DAL_CLIENT.get_or_init(|| AppDalClient::new(app_dal_conf)); Ok(()) } diff --git a/apps/recorder/src/extract/mikan/client.rs b/apps/recorder/src/extract/mikan/client.rs index 5c5521e..5ae9ee4 100644 --- a/apps/recorder/src/extract/mikan/client.rs +++ b/apps/recorder/src/extract/mikan/client.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use loco_rs::app::{AppContext, Initializer}; use once_cell::sync::OnceCell; -use super::{AppMikanConfig, MIKAN_BASE_URL}; +use super::AppMikanConfig; use crate::{config::AppConfigExt, fetch::HttpClient}; static APP_MIKAN_CLIENT: OnceCell = OnceCell::new(); @@ -14,12 +14,10 @@ pub struct AppMikanClient { } impl AppMikanClient { - pub fn new(mut config: AppMikanConfig) -> loco_rs::Result { + pub fn new(config: AppMikanConfig) -> loco_rs::Result { let http_client = - HttpClient::new(config.http_client.take()).map_err(loco_rs::Error::wrap)?; - let base_url = config - .base_url - .unwrap_or_else(|| String::from(MIKAN_BASE_URL)); + HttpClient::from_config(config.http_client).map_err(loco_rs::Error::wrap)?; + let base_url = config.base_url; Ok(Self { http_client, base_url, @@ -55,7 +53,7 @@ impl Initializer for AppMikanClientInitializer { async fn before_run(&self, app_context: &AppContext) -> loco_rs::Result<()> { let config = &app_context.config; - let app_mikan_conf = config.get_app_conf()?.mikan.unwrap_or_default(); + let app_mikan_conf = config.get_app_conf()?.mikan; APP_MIKAN_CLIENT.get_or_try_init(|| AppMikanClient::new(app_mikan_conf))?; diff --git a/apps/recorder/src/extract/mikan/config.rs b/apps/recorder/src/extract/mikan/config.rs index 8335cd2..228df62 100644 --- a/apps/recorder/src/extract/mikan/config.rs +++ b/apps/recorder/src/extract/mikan/config.rs @@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize}; use crate::fetch::HttpClientConfig; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AppMikanConfig { - pub http_client: Option, - pub base_url: Option, + pub http_client: HttpClientConfig, + pub base_url: String, } diff --git a/apps/recorder/src/extract/mikan/rss_parser.rs b/apps/recorder/src/extract/mikan/rss_parser.rs index 85dcad4..0123ad1 100644 --- a/apps/recorder/src/extract/mikan/rss_parser.rs +++ b/apps/recorder/src/extract/mikan/rss_parser.rs @@ -1,17 +1,17 @@ use std::ops::Deref; use chrono::DateTime; +use dlsignal::core::BITTORRENT_MIME_TYPE; use itertools::Itertools; use reqwest::IntoUrl; use serde::{Deserialize, Serialize}; -use torrent::core::BITTORRENT_MIME_TYPE; use url::Url; use super::{ web_parser::{parse_mikan_episode_id_from_homepage, MikanEpisodeHomepage}, AppMikanClient, }; -use crate::{extract::errors::ParseError, fetch::bytes::download_bytes_with_client}; +use crate::{extract::errors::ParseError, fetch::bytes::fetch_bytes}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct MikanRssItem { @@ -228,7 +228,7 @@ pub async fn parse_mikan_rss_channel_from_rss_link( url: impl IntoUrl, ) -> eyre::Result { let http_client = client.map(|s| s.deref()); - let bytes = download_bytes_with_client(http_client, url.as_str()).await?; + let bytes = fetch_bytes(http_client, url.as_str()).await?; let channel = rss::Channel::read_from(&bytes[..])?; @@ -297,7 +297,7 @@ pub async fn parse_mikan_rss_channel_from_rss_link( mod tests { use std::assert_matches::assert_matches; - use torrent::core::BITTORRENT_MIME_TYPE; + use dlsignal::core::BITTORRENT_MIME_TYPE; use crate::extract::mikan::{ parse_mikan_rss_channel_from_rss_link, MikanBangumiAggregationRssChannel, diff --git a/apps/recorder/src/extract/mikan/web_parser.rs b/apps/recorder/src/extract/mikan/web_parser.rs index e0c8102..a61c54a 100644 --- a/apps/recorder/src/extract/mikan/web_parser.rs +++ b/apps/recorder/src/extract/mikan/web_parser.rs @@ -18,7 +18,7 @@ use crate::{ app::AppContextExt, dal::DalContentCategory, extract::html::parse_style_attr, - fetch::{html::download_html_with_client, image::download_image_with_client}, + fetch::{html::fetch_html, image::fetch_image}, models::subscribers, }; @@ -95,7 +95,7 @@ pub async fn parse_mikan_bangumi_poster_from_origin_poster_src( origin_poster_src: Url, ) -> eyre::Result { let http_client = client.map(|s| s.deref()); - let poster_data = download_image_with_client(http_client, origin_poster_src.clone()).await?; + let poster_data = fetch_image(http_client, origin_poster_src.clone()).await?; Ok(MikanBangumiPosterMeta { origin_poster_src, poster_data: Some(poster_data), @@ -127,8 +127,7 @@ pub async fn parse_mikan_bangumi_poster_from_origin_poster_src_with_cache( }); } - let poster_data = - download_image_with_client(Some(mikan_client.deref()), origin_poster_src.clone()).await?; + let poster_data = fetch_image(Some(mikan_client.deref()), origin_poster_src.clone()).await?; let poster_str = dal_client .store_object( @@ -153,7 +152,7 @@ pub async fn parse_mikan_bangumi_meta_from_mikan_homepage( ) -> eyre::Result { let http_client = client.map(|s| s.deref()); let url_host = url.origin().unicode_serialization(); - let content = download_html_with_client(http_client, url.as_str()).await?; + let content = fetch_html(http_client, url.as_str()).await?; let html = Html::parse_document(&content); let bangumi_fansubs = html @@ -276,7 +275,7 @@ pub async fn parse_mikan_episode_meta_from_mikan_homepage( ) -> eyre::Result { let http_client = client.map(|s| s.deref()); let url_host = url.origin().unicode_serialization(); - let content = download_html_with_client(http_client, url.as_str()).await?; + let content = fetch_html(http_client, url.as_str()).await?; let html = Html::parse_document(&content); @@ -401,6 +400,8 @@ pub async fn parse_mikan_episode_meta_from_mikan_homepage( }) } +pub async fn parse_mikan_bangumis_from_user_home(_client: Option<&AppMikanClient>, _url: Url) {} + #[cfg(test)] mod test { use std::assert_matches::assert_matches; diff --git a/apps/recorder/src/fetch/bytes.rs b/apps/recorder/src/fetch/bytes.rs index cee96bb..a0a71ab 100644 --- a/apps/recorder/src/fetch/bytes.rs +++ b/apps/recorder/src/fetch/bytes.rs @@ -1,24 +1,11 @@ use bytes::Bytes; use reqwest::IntoUrl; -use super::{core::DEFAULT_HTTP_CLIENT_USER_AGENT, HttpClient}; +use super::HttpClient; -pub async fn download_bytes(url: T) -> eyre::Result { - let request_client = reqwest::Client::builder() - .user_agent(DEFAULT_HTTP_CLIENT_USER_AGENT) - .build()?; - let bytes = request_client.get(url).send().await?.bytes().await?; +pub async fn fetch_bytes(client: Option<&HttpClient>, url: T) -> eyre::Result { + let client = client.unwrap_or_default(); + + let bytes = client.get(url).send().await?.bytes().await?; Ok(bytes) } - -pub async fn download_bytes_with_client( - client: Option<&HttpClient>, - url: T, -) -> eyre::Result { - if let Some(client) = client { - let bytes = client.get(url).send().await?.bytes().await?; - Ok(bytes) - } else { - download_bytes(url).await - } -} diff --git a/apps/recorder/src/fetch/client.rs b/apps/recorder/src/fetch/client.rs index 6007975..51031a5 100644 --- a/apps/recorder/src/fetch/client.rs +++ b/apps/recorder/src/fetch/client.rs @@ -2,6 +2,7 @@ use std::{ops::Deref, time::Duration}; use axum::http::Extensions; use leaky_bucket::RateLimiter; +use once_cell::sync::OnceCell; use reqwest::{ClientBuilder, Request, Response}; use reqwest_middleware::{ ClientBuilder as ClientWithMiddlewareBuilder, ClientWithMiddleware, Next, @@ -11,7 +12,7 @@ use reqwest_tracing::TracingMiddleware; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use super::DEFAULT_HTTP_CLIENT_USER_AGENT; +use crate::fetch::DEFAULT_HTTP_CLIENT_USER_AGENT; #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] @@ -27,6 +28,7 @@ pub struct HttpClientConfig { pub struct HttpClient { client: ClientWithMiddleware, + pub config: HttpClientConfig, } impl Deref for HttpClient { @@ -55,42 +57,73 @@ impl reqwest_middleware::Middleware for RateLimiterMiddleware { } impl HttpClient { - pub fn new(config: Option) -> reqwest::Result { - let mut config = config.unwrap_or_default(); - let retry_policy = ExponentialBackoff::builder() - .build_with_max_retries(config.exponential_backoff_max_retries.take().unwrap_or(3)); - let rate_limiter = RateLimiter::builder() - .max(config.leaky_bucket_max_tokens.take().unwrap_or(3) as usize) - .initial( - config - .leaky_bucket_initial_tokens - .take() - .unwrap_or_default() as usize, - ) - .refill(config.leaky_bucket_refill_tokens.take().unwrap_or(1) as usize) - .interval( - config - .leaky_bucket_refill_interval - .take() - .unwrap_or_else(|| Duration::from_millis(500)), - ) - .build(); + pub fn from_config(config: HttpClientConfig) -> reqwest::Result { + let reqwest_client_builder = ClientBuilder::new().user_agent( + config + .user_agent + .as_deref() + .unwrap_or(DEFAULT_HTTP_CLIENT_USER_AGENT), + ); - let client = ClientBuilder::new() - .user_agent( - config - .user_agent - .take() - .unwrap_or_else(|| DEFAULT_HTTP_CLIENT_USER_AGENT.to_owned()), - ) - .build()?; + let reqwest_client = reqwest_client_builder.build()?; + + let mut reqwest_with_middleware_builder = + ClientWithMiddlewareBuilder::new(reqwest_client).with(TracingMiddleware::default()); + + if let Some(ref x) = config.exponential_backoff_max_retries { + let retry_policy = ExponentialBackoff::builder().build_with_max_retries(*x); + + reqwest_with_middleware_builder = reqwest_with_middleware_builder + .with(RetryTransientMiddleware::new_with_policy(retry_policy)); + } + + if let (None, None, None, None) = ( + config.leaky_bucket_initial_tokens.as_ref(), + config.leaky_bucket_refill_tokens.as_ref(), + config.leaky_bucket_refill_interval.as_ref(), + config.leaky_bucket_max_tokens.as_ref(), + ) { + } else { + let mut rate_limiter_builder = RateLimiter::builder(); + + if let Some(ref x) = config.leaky_bucket_max_tokens { + rate_limiter_builder.max(*x as usize); + } + if let Some(ref x) = config.leaky_bucket_initial_tokens { + rate_limiter_builder.initial(*x as usize); + } + if let Some(ref x) = config.leaky_bucket_refill_tokens { + rate_limiter_builder.refill(*x as usize); + } + if let Some(ref x) = config.leaky_bucket_refill_interval { + rate_limiter_builder.interval(*x); + } + + let rate_limiter = rate_limiter_builder.build(); + + reqwest_with_middleware_builder = + reqwest_with_middleware_builder.with(RateLimiterMiddleware { rate_limiter }); + } + + let reqwest_with_middleware = reqwest_with_middleware_builder.build(); Ok(Self { - client: ClientWithMiddlewareBuilder::new(client) - .with(TracingMiddleware::default()) - .with(RateLimiterMiddleware { rate_limiter }) - .with(RetryTransientMiddleware::new_with_policy(retry_policy)) - .build(), + client: reqwest_with_middleware, + config, }) } } + +static DEFAULT_HTTP_CLIENT: OnceCell = OnceCell::new(); + +impl Default for HttpClient { + fn default() -> Self { + HttpClient::from_config(Default::default()).expect("Failed to create default HttpClient") + } +} + +impl Default for &HttpClient { + fn default() -> Self { + DEFAULT_HTTP_CLIENT.get_or_init(HttpClient::default) + } +} diff --git a/apps/recorder/src/fetch/html.rs b/apps/recorder/src/fetch/html.rs index ab6ce9f..2fe4442 100644 --- a/apps/recorder/src/fetch/html.rs +++ b/apps/recorder/src/fetch/html.rs @@ -1,23 +1,10 @@ use reqwest::IntoUrl; -use super::{core::DEFAULT_HTTP_CLIENT_USER_AGENT, HttpClient}; +use super::HttpClient; + +pub async fn fetch_html(client: Option<&HttpClient>, url: T) -> eyre::Result { + let client = client.unwrap_or_default(); + let content = client.get(url).send().await?.text().await?; -pub async fn download_html(url: U) -> eyre::Result { - let request_client = reqwest::Client::builder() - .user_agent(DEFAULT_HTTP_CLIENT_USER_AGENT) - .build()?; - let content = request_client.get(url).send().await?.text().await?; Ok(content) } - -pub async fn download_html_with_client( - client: Option<&HttpClient>, - url: T, -) -> eyre::Result { - if let Some(client) = client { - let content = client.get(url).send().await?.text().await?; - Ok(content) - } else { - download_html(url).await - } -} diff --git a/apps/recorder/src/fetch/image.rs b/apps/recorder/src/fetch/image.rs index 454f57d..1b5bb0e 100644 --- a/apps/recorder/src/fetch/image.rs +++ b/apps/recorder/src/fetch/image.rs @@ -1,18 +1,8 @@ use bytes::Bytes; use reqwest::IntoUrl; -use super::{ - bytes::{download_bytes, download_bytes_with_client}, - HttpClient, -}; +use super::{bytes::fetch_bytes, HttpClient}; -pub async fn download_image(url: U) -> eyre::Result { - download_bytes(url).await -} - -pub async fn download_image_with_client( - client: Option<&HttpClient>, - url: T, -) -> eyre::Result { - download_bytes_with_client(client, url).await +pub async fn fetch_image(client: Option<&HttpClient>, url: T) -> eyre::Result { + fetch_bytes(client, url).await } diff --git a/apps/recorder/src/fetch/mod.rs b/apps/recorder/src/fetch/mod.rs index 1c110a5..79bcc23 100644 --- a/apps/recorder/src/fetch/mod.rs +++ b/apps/recorder/src/fetch/mod.rs @@ -6,6 +6,7 @@ pub mod image; pub use core::DEFAULT_HTTP_CLIENT_USER_AGENT; -pub use bytes::download_bytes; +pub use bytes::fetch_bytes; pub use client::{HttpClient, HttpClientConfig}; -pub use image::download_image; +pub use html::fetch_html; +pub use image::fetch_image; diff --git a/apps/storybook/README.md b/apps/storybook/README.md index ef0e47e..a5cbf96 100644 --- a/apps/storybook/README.md +++ b/apps/storybook/README.md @@ -14,11 +14,11 @@ pnpm dev bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open [http://localhost:5000](http://localhost:5000) with your browser to see the result. You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. -[API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. +[API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) can be accessed on [http://localhost:5000/api/hello](http://localhost:5000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) instead of React pages. diff --git a/apps/web/.env.development b/apps/web/.env.development index 52a6723..a1fee8a 100644 --- a/apps/web/.env.development +++ b/apps/web/.env.development @@ -9,7 +9,7 @@ SVIX_TOKEN="" LIVEBLOCKS_SECRET="" # Client -NEXT_PUBLIC_APP_URL="http://localhost:3000" -NEXT_PUBLIC_WEB_URL="http://localhost:3001" -NEXT_PUBLIC_DOCS_URL="http://localhost:3004" -NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://webui.konobangu.com" \ No newline at end of file +NEXT_PUBLIC_APP_URL="http://localhost:5000" +NEXT_PUBLIC_WEB_URL="http://localhost:5001" +NEXT_PUBLIC_DOCS_URL="http://localhost:5004" +NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com" \ No newline at end of file diff --git a/apps/web/.env.example b/apps/web/.env.example index ca7d1ee..ffce2a3 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -9,7 +9,7 @@ SVIX_TOKEN="" LIVEBLOCKS_SECRET="" # Client -NEXT_PUBLIC_APP_URL="http://localhost:3000" -NEXT_PUBLIC_WEB_URL="http://localhost:3001" -NEXT_PUBLIC_DOCS_URL="http://localhost:3004" -NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="http://localhost:3000" \ No newline at end of file +NEXT_PUBLIC_APP_URL="http://localhost:5000" +NEXT_PUBLIC_WEB_URL="http://localhost:5001" +NEXT_PUBLIC_DOCS_URL="http://localhost:5004" +NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://konobangu.com" \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index cb86be1..eec5cca 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -2,7 +2,7 @@ "name": "web", "private": true, "scripts": { - "dev": "next dev -p 3001 --turbopack", + "dev": "next dev -p 5001 --turbopack", "build": "next build", "start": "next start", "analyze": "ANALYZE=true pnpm build", diff --git a/config/test.yaml b/config/test.yaml index 3d1fcf4..ca7528b 100644 --- a/config/test.yaml +++ b/config/test.yaml @@ -17,7 +17,7 @@ logger: # Web server configuration server: # Port on which the server will listen. the server binding is 0.0.0.0:{PORT} - port: 3001 + port: 5001 # The UI hostname or IP address that mailers will point to. host: http://webui.konobangu.com # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block diff --git a/docker-compose.yaml b/docker-compose.yaml index 4af7834..c5f4998 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -4,7 +4,7 @@ services: webui: image: node:22-alpine ports: - - '3000:3000' + - '5000:5000' volumes: - ./apps/webui:/home/node/app - node_modules:/home/node/app/node_modules diff --git a/packages/torrent/.gitignore b/packages/dlsignal/.gitignore similarity index 100% rename from packages/torrent/.gitignore rename to packages/dlsignal/.gitignore diff --git a/packages/torrent/Cargo.toml b/packages/dlsignal/Cargo.toml similarity index 95% rename from packages/torrent/Cargo.toml rename to packages/dlsignal/Cargo.toml index 1af0185..bd3f3e1 100644 --- a/packages/torrent/Cargo.toml +++ b/packages/dlsignal/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "torrent" +name = "dlsignal" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -name = "torrent" +name = "dlsignal" path = "src/lib.rs" [features] diff --git a/packages/torrent/src/core.rs b/packages/dlsignal/src/core.rs similarity index 100% rename from packages/torrent/src/core.rs rename to packages/dlsignal/src/core.rs diff --git a/packages/torrent/src/error.rs b/packages/dlsignal/src/error.rs similarity index 100% rename from packages/torrent/src/error.rs rename to packages/dlsignal/src/error.rs diff --git a/packages/torrent/src/lib.rs b/packages/dlsignal/src/lib.rs similarity index 100% rename from packages/torrent/src/lib.rs rename to packages/dlsignal/src/lib.rs diff --git a/packages/torrent/src/qbit.rs b/packages/dlsignal/src/qbit.rs similarity index 100% rename from packages/torrent/src/qbit.rs rename to packages/dlsignal/src/qbit.rs diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 769b8ed..a5a5e55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,70 +49,6 @@ importers: specifier: ^4.1.12 version: 4.1.14 - apps/api: - dependencies: - '@konobangu/analytics': - specifier: workspace:* - version: link:../../packages/analytics - '@konobangu/auth': - specifier: workspace:* - version: link:../../packages/auth - '@konobangu/database': - specifier: workspace:* - version: link:../../packages/database - '@konobangu/design-system': - specifier: workspace:* - version: link:../../packages/design-system - '@konobangu/env': - specifier: workspace:* - version: link:../../packages/env - '@konobangu/next-config': - specifier: workspace:* - version: link:../../packages/next-config - '@konobangu/observability': - specifier: workspace:* - version: link:../../packages/observability - '@sentry/nextjs': - specifier: ^8.43.0 - version: 8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(next@15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.97.1) - import-in-the-middle: - specifier: ^1.11.3 - version: 1.12.0 - next: - specifier: ^15.1.3 - version: 15.1.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) - react: - specifier: ^19.0.0 - version: 19.0.0 - react-dom: - specifier: ^19.0.0 - version: 19.0.0(react@19.0.0) - require-in-the-middle: - specifier: ^7.4.0 - version: 7.4.0 - svix: - specifier: ^1.43.0 - version: 1.44.0 - devDependencies: - '@konobangu/typescript-config': - specifier: workspace:* - version: link:../../packages/typescript-config - '@types/node': - specifier: 22.10.1 - version: 22.10.1 - '@types/react': - specifier: 19.0.1 - version: 19.0.1 - '@types/react-dom': - specifier: 19.0.2 - version: 19.0.2(@types/react@19.0.1) - concurrently: - specifier: ^9.1.0 - version: 9.1.1 - typescript: - specifier: ^5.7.2 - version: 5.7.2 - apps/app: dependencies: '@konobangu/analytics': @@ -5476,10 +5412,6 @@ packages: cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -5586,11 +5518,6 @@ packages: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} engines: {'0': node >= 0.8} - concurrently@9.1.1: - resolution: {integrity: sha512-6VX8lrBIycgZKTwBsWS+bLrmkGRkDmvtGsYylRN9b93CygN6CbK46HmnQ3rdSOR8HRjdahDrxb5MqD9cEFOg5Q==} - engines: {node: '>=18'} - hasBin: true - console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} @@ -9090,10 +9017,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.2: - resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} - engines: {node: '>= 0.4'} - shelljs@0.8.5: resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} engines: {node: '>=4'} @@ -9596,10 +9519,6 @@ packages: resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -10221,10 +10140,6 @@ packages: y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} @@ -10244,18 +10159,10 @@ packages: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -15191,12 +15098,6 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - clone@1.0.4: {} clone@2.1.2: {} @@ -15289,16 +15190,6 @@ snapshots: readable-stream: 2.3.8 typedarray: 0.0.6 - concurrently@9.1.1: - dependencies: - chalk: 4.1.2 - lodash: 4.17.21 - rxjs: 7.8.1 - shell-quote: 1.8.2 - supports-color: 8.1.1 - tree-kill: 1.2.2 - yargs: 17.7.2 - console-browserify@1.2.0: {} constant-case@2.0.0: @@ -19605,8 +19496,6 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.2: {} - shelljs@0.8.5: dependencies: glob: 7.2.3 @@ -20266,8 +20155,6 @@ snapshots: dependencies: punycode: 2.3.1 - tree-kill@1.2.2: {} - trim-lines@3.0.1: {} trough@2.2.0: {} @@ -21029,8 +20916,6 @@ snapshots: y18n@4.0.3: {} - y18n@5.0.8: {} - yallist@2.1.2: {} yallist@3.1.1: {} @@ -21044,8 +20929,6 @@ snapshots: camelcase: 5.3.1 decamelize: 1.2.0 - yargs-parser@21.1.1: {} - yargs@15.4.1: dependencies: cliui: 6.0.0 @@ -21060,16 +20943,6 @@ snapshots: y18n: 4.0.3 yargs-parser: 18.1.3 - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - yn@3.1.1: {} yocto-queue@0.1.0: {}