diff --git a/.cargo/config.toml b/.cargo/config.toml index 88b49d2..408636c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,32 +2,9 @@ recorder-playground = "run -p recorder --example playground -- --environment development" [build] -rustflags = ["-Zthreads=8", "--cfg", "feature=\"testcontainers\""] - -[target.x86_64-unknown-linux-gnu] -linker = "clang" -rustflags = ["-Zthreads=8", "-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"] - -[target.x86_64-pc-windows-msvc] -linker = "rust-lld.exe" -rustflags = ["-Zthreads=8", "-Zshare-generics=n"] - -# NOTE: you must install [Mach-O LLD Port](https://lld.llvm.org/MachO/index.html) on mac. you can easily do this by installing llvm which includes lld with the "brew" package manager: -# `brew install llvm` -#[target.x86_64-apple-darwin] -#rustflags = [ -# "-Zthreads=8", -# "-C", -# "link-arg=-fuse-ld=/usr/local/opt/llvm/bin/ld64.lld", -# "-Zshare-generics=y", -#] - -# NOTE: you must install [Mach-O LLD Port](https://lld.llvm.org/MachO/index.html) on mac. you can easily do this by installing llvm which includes lld with the "brew" package manager: -# `brew install llvm` -#[target.aarch64-apple-darwin] -#rustflags = [ -# "-Zthreads=8", -# "-C", -# "link-arg=-fuse-ld=/opt/homebrew/opt/llvm/bin/ld64.lld", -# "-Zshare-generics=y", -#] +rustflags = [ + "-Zthreads=8", + "--cfg", + "feature=\"testcontainers\"", + "-Zshare-generics=y", +] diff --git a/.vscode/settings.json b/.vscode/settings.json index 5788fae..1637c31 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -28,5 +28,18 @@ "emmet.showExpandedAbbreviation": "never", "prettier.enable": false, "typescript.tsdk": "node_modules/typescript/lib", - "rust-analyzer.cargo.features": ["testcontainers"] + "rust-analyzer.cargo.features": [ + "testcontainers" + ], + "sqltools.connections": [ + { + "previewLimit": 50, + "server": "localhost", + "port": 5432, + "driver": "PostgreSQL", + "name": "konobangu-dev", + "database": "konobangu", + "username": "konobangu" + } + ] } diff --git a/Cargo.lock b/Cargo.lock index 9160185..0d14cd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5632,8 +5632,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "seaography" version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f5e0455935e4f31eb64ce606d9963715efd4c1856edb129619126f6b5372fcf" +source = "git+https://github.com/lonelyhentxi/seaography.git?rev=0c4cc5c#0c4cc5ccd43730338802f98401120d5faeccd096" dependencies = [ "async-graphql", "fnv", diff --git a/Cargo.toml b/Cargo.toml index 3c20fb2..509934e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,3 +50,4 @@ recorder = { path = "./apps/recorder" } [patch.crates-io] jwt-authorizer = { git = "https://github.com/blablacio/jwt-authorizer.git", rev = "e956774" } +seaography = { git = "https://github.com/lonelyhentxi/seaography.git", rev = "0c4cc5c" } diff --git a/apps/recorder/.env b/apps/recorder/.env index 60789ec..455bbef 100644 --- a/apps/recorder/.env +++ b/apps/recorder/.env @@ -1,8 +1,8 @@ -AUTH_TYPE = "basic" # or oidc +AUTH_TYPE = "oidc" # or oidc BASIC_USER = "konobangu" BASIC_PASSWORD = "konobangu" -# OIDC_ISSUER="https://auth.logto.io/oidc" -# OIDC_API_AUDIENCE = "https://konobangu.com/api" -# OIDC_CLIENT_ID = "client_id" -# OIDC_CLIENT_SECRET = "client_secret" # optional -# OIDC_EXTRA_SCOPES = "read:konobangu write:konobangu" +OIDC_ISSUER="https://auth.logto.io/oidc" +OIDC_AUDIENCE = "https://konobangu.com/api" +OIDC_CLIENT_ID = "client_id" +OIDC_CLIENT_SECRET = "client_secret" # optional +OIDC_EXTRA_SCOPES = "read:konobangu write:konobangu" diff --git a/apps/recorder/src/graphql/schema_root.rs b/apps/recorder/src/graphql/schema_root.rs index 77658eb..a7c518e 100644 --- a/apps/recorder/src/graphql/schema_root.rs +++ b/apps/recorder/src/graphql/schema_root.rs @@ -3,13 +3,16 @@ use once_cell::sync::OnceCell; use sea_orm::{DatabaseConnection, EntityTrait, Iterable}; use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper}; -use super::{ - filter::{SUBSCRIBER_ID_FILTER_INFO, subscriber_id_condition_function}, +use crate::graphql::{ + extentions::AuthExtensionFactory, + filter::{ + SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info, subscriber_id_condition_function, + }, + guard::guard_entity_with_subscriber_id, util::{get_entity_column_key, get_entity_key}, }; -use crate::graphql::{filter::init_custom_filter_info, guard::guard_entity_with_subscriber_id}; -static CONTEXT: OnceCell = OnceCell::new(); +pub static CONTEXT: OnceCell = OnceCell::new(); fn restrict_filter_input_for_entity( context: &mut BuilderContext, @@ -153,7 +156,9 @@ pub fn schema( }; schema .data(database) - // .extension(GraphqlAuthExtension) + .extension(AuthExtensionFactory { + builder_context: context, + }) .finish() .inspect_err(|e| tracing::error!(e = ?e)) } diff --git a/apps/webui/.env b/apps/webui/.env index 41e2f08..b28612b 100644 --- a/apps/webui/.env +++ b/apps/webui/.env @@ -5,7 +5,7 @@ AUTH_TYPE = "basic" # or oidc BASIC_USER = "konobangu" BASIC_PASSWORD = "konobangu" # OIDC_ISSUER="https://auth.logto.io/oidc" -# OIDC_API_AUDIENCE = "https://konobangu.com/api" +# OIDC_AUDIENCE = "https://konobangu.com/api" # OIDC_CLIENT_ID = "client_id" # OIDC_CLIENT_SECRET = "client_secret" # optional # OIDC_EXTRA_SCOPES = "read:konobangu write:konobangu" diff --git a/apps/webui/src/auth/context.ts b/apps/webui/src/auth/context.ts index eba92e7..8706b2e 100644 --- a/apps/webui/src/auth/context.ts +++ b/apps/webui/src/auth/context.ts @@ -42,7 +42,7 @@ export interface OidcAuthContext { type: typeof AuthMethodEnum.OIDC; oidcSecurityService: OidcSecurityService; isAuthenticated$: Observable; - userData$: Observable; + userData$: Observable<{}>; checkAuthResultEvent$: Observable; } diff --git a/apps/webui/src/routes/_app/subscriptions/create.tsx b/apps/webui/src/routes/_app/subscriptions/create.tsx index ebe6456..35d62b9 100644 --- a/apps/webui/src/routes/_app/subscriptions/create.tsx +++ b/apps/webui/src/routes/_app/subscriptions/create.tsx @@ -1,5 +1,37 @@ +import { useAuth } from '@/auth/hooks'; +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; import type { RouteStateDataOption } from '@/traits/router'; import { createFileRoute } from '@tanstack/react-router'; +import { useNavigate } from '@tanstack/react-router'; +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { toast } from 'sonner'; export const Route = createFileRoute('/_app/subscriptions/create')({ component: SubscriptionCreateRouteComponent, @@ -8,6 +40,198 @@ export const Route = createFileRoute('/_app/subscriptions/create')({ } satisfies RouteStateDataOption, }); +type SubscriptionFormValues = { + displayName: string; + sourceUrl: string; + category: string; + enabled: boolean; +}; + function SubscriptionCreateRouteComponent() { - return
Hello "/subscriptions/create"!
; + const { userData } = useAuth(); + console.log(JSON.stringify(userData, null, 2)); + const [isSubmitting, setIsSubmitting] = useState(false); + const navigate = useNavigate(); + const form = useForm({ + defaultValues: { + displayName: '', + sourceUrl: '', + category: 'Mikan', + enabled: true, + }, + }); + + const onSubmit = async (data: SubscriptionFormValues) => { + try { + setIsSubmitting(true); + const requestData = { + query: ` + mutation CreateSubscription($input: SubscriptionsInsertInput!) { + subscriptionsCreateOne(data: $input) { + id + displayName + sourceUrl + enabled + category + } + } + `, + variables: { + input: { + category: data.category, + displayName: data.displayName, + sourceUrl: data.sourceUrl, + enabled: data.enabled, + }, + }, + }; + + const response = await fetch('/api/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestData), + }); + + const responseData = await response.json(); + if (responseData.errors) { + throw new Error( + responseData.errors[0]?.message || 'Failed to create subscription' + ); + } + + toast.success('Subscription created successfully'); + navigate({ to: '/subscriptions/manage' }); + } catch (error) { + console.error('Failed to create subscription:', error); + toast.error( + `Subscription creation failed: ${ + error instanceof Error ? error.message : 'Unknown error' + }` + ); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + + Create Anime Subscription + Add a new anime subscription source + + +
+ + ( + + Source Type + + + Currently only Mikan source is supported + + + + )} + /> + + ( + + Display Name + + + + + Set an easily recognizable name for this subscription + + + + )} + /> + + ( + + Source URL + + + + + Copy the RSS subscription link from the source website, e.g. + https://mikanani.me/RSS/Bangumi?bangumiId=3141&subgroupid=370 + + + + )} + /> + + ( + +
+ + Enable Subscription + + + Enable this subscription immediately after creation + +
+ + + +
+ )} + /> + + +
+ + + + +
+ ); }