feat: implement authentication and user management features
Some checks failed
Frontend CI / build (push) Failing after 58s
Some checks failed
Frontend CI / build (push) Failing after 58s
This commit is contained in:
parent
1ae104cd56
commit
365c979878
@ -1 +1 @@
|
|||||||
PUBLIC_API_BASE_URL=http://127.0.0.1:5173/api/
|
PUBLIC_API_BASE_URL=http://localhost:5173/api/
|
||||||
|
1
frontend/src/app.d.ts
vendored
1
frontend/src/app.d.ts
vendored
@ -5,6 +5,7 @@ declare global {
|
|||||||
// interface Error {}
|
// interface Error {}
|
||||||
|
|
||||||
interface Locals {
|
interface Locals {
|
||||||
|
authBloc: import('$lib/auth/adapter/presenter/authBloc').AuthBloc;
|
||||||
postListBloc: import('$lib/post/adapter/presenter/postListBloc').PostListBloc;
|
postListBloc: import('$lib/post/adapter/presenter/postListBloc').PostListBloc;
|
||||||
postBloc: import('$lib/post/adapter/presenter/postBloc').PostBloc;
|
postBloc: import('$lib/post/adapter/presenter/postBloc').PostBloc;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,14 @@ import { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase';
|
|||||||
import { PostApiServiceImpl } from '$lib/post/framework/api/postApiServiceImpl';
|
import { PostApiServiceImpl } from '$lib/post/framework/api/postApiServiceImpl';
|
||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
import { Environment } from '$lib/environment';
|
import { Environment } from '$lib/environment';
|
||||||
|
import { AuthApiServiceImpl } from '$lib/auth/framework/api/authApiServiceImpl';
|
||||||
|
import { AuthRepositoryImpl } from '$lib/auth/adapter/gateway/authRepositoryImpl';
|
||||||
|
import type { AuthApiService } from '$lib/auth/adapter/gateway/authApiService';
|
||||||
|
import type { AuthRepository } from '$lib/auth/application/gateway/authRepository';
|
||||||
|
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
||||||
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
|
import { AuthBloc } from '$lib/auth/adapter/presenter/authBloc';
|
||||||
|
import { GetCurrentUserUseCase } from '$lib/auth/application/useCase/getCurrentUserUseCase';
|
||||||
|
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: Environment.SENTRY_DSN,
|
dsn: Environment.SENTRY_DSN,
|
||||||
@ -16,11 +24,16 @@ Sentry.init({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const handle: Handle = sequence(Sentry.sentryHandle(), ({ event, resolve }) => {
|
export const handle: Handle = sequence(Sentry.sentryHandle(), ({ event, resolve }) => {
|
||||||
const postApiService = new PostApiServiceImpl(event.fetch);
|
const authApiService: AuthApiService = new AuthApiServiceImpl(event.fetch);
|
||||||
const postRepository = new PostRepositoryImpl(postApiService);
|
const authRepository: AuthRepository = new AuthRepositoryImpl(authApiService);
|
||||||
|
const getCurrentUserUseCase = new GetCurrentUserUseCase(authRepository);
|
||||||
|
|
||||||
|
const postApiService: PostApiService = new PostApiServiceImpl(event.fetch);
|
||||||
|
const postRepository: PostRepository = new PostRepositoryImpl(postApiService);
|
||||||
const getAllPostsUseCase = new GetAllPostsUseCase(postRepository);
|
const getAllPostsUseCase = new GetAllPostsUseCase(postRepository);
|
||||||
const getPostUseCase = new GetPostUseCase(postRepository);
|
const getPostUseCase = new GetPostUseCase(postRepository);
|
||||||
|
|
||||||
|
event.locals.authBloc = new AuthBloc(getCurrentUserUseCase);
|
||||||
event.locals.postListBloc = new PostListBloc(getAllPostsUseCase);
|
event.locals.postListBloc = new PostListBloc(getAllPostsUseCase);
|
||||||
event.locals.postBloc = new PostBloc(getPostUseCase);
|
event.locals.postBloc = new PostBloc(getPostUseCase);
|
||||||
|
|
||||||
|
5
frontend/src/lib/auth/adapter/gateway/authApiService.ts
Normal file
5
frontend/src/lib/auth/adapter/gateway/authApiService.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type { UserResponseDto } from '$lib/auth/adapter/gateway/userResponseDto';
|
||||||
|
|
||||||
|
export interface AuthApiService {
|
||||||
|
getCurrentUser(): Promise<UserResponseDto | null>;
|
||||||
|
}
|
12
frontend/src/lib/auth/adapter/gateway/authRepositoryImpl.ts
Normal file
12
frontend/src/lib/auth/adapter/gateway/authRepositoryImpl.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import type { AuthApiService } from '$lib/auth/adapter/gateway/authApiService';
|
||||||
|
import type { AuthRepository } from '$lib/auth/application/gateway/authRepository';
|
||||||
|
import type { User } from '$lib/auth/domain/entity/user';
|
||||||
|
|
||||||
|
export class AuthRepositoryImpl implements AuthRepository {
|
||||||
|
constructor(private readonly authApiService: AuthApiService) {}
|
||||||
|
|
||||||
|
async getCurrentUser(): Promise<User | null> {
|
||||||
|
const result = await this.authApiService.getCurrentUser();
|
||||||
|
return result?.toEntity() ?? null;
|
||||||
|
}
|
||||||
|
}
|
37
frontend/src/lib/auth/adapter/gateway/userResponseDto.ts
Normal file
37
frontend/src/lib/auth/adapter/gateway/userResponseDto.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { User } from '$lib/auth/domain/entity/user';
|
||||||
|
import z from 'zod';
|
||||||
|
|
||||||
|
export const UserResponseSchema = z.object({
|
||||||
|
id: z.int32(),
|
||||||
|
displayed_name: z.string(),
|
||||||
|
email: z.email(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export class UserResponseDto {
|
||||||
|
readonly id: number;
|
||||||
|
readonly displayedName: string;
|
||||||
|
readonly email: string;
|
||||||
|
|
||||||
|
private constructor(props: { id: number; displayedName: string; email: string }) {
|
||||||
|
this.id = props.id;
|
||||||
|
this.displayedName = props.displayedName;
|
||||||
|
this.email = props.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json: unknown): UserResponseDto {
|
||||||
|
const parsedJson = UserResponseSchema.parse(json);
|
||||||
|
return new UserResponseDto({
|
||||||
|
id: parsedJson.id,
|
||||||
|
displayedName: parsedJson.displayed_name,
|
||||||
|
email: parsedJson.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toEntity(): User {
|
||||||
|
return new User({
|
||||||
|
id: this.id,
|
||||||
|
name: this.displayedName,
|
||||||
|
email: this.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
59
frontend/src/lib/auth/adapter/presenter/authBloc.ts
Normal file
59
frontend/src/lib/auth/adapter/presenter/authBloc.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { AuthViewModel } from '$lib/auth/adapter/presenter/authViewModel';
|
||||||
|
import { UserViewModel } from '$lib/auth/adapter/presenter/userViewModel';
|
||||||
|
import type { GetCurrentUserUseCase } from '$lib/auth/application/useCase/getCurrentUserUseCase';
|
||||||
|
import { StatusType, type AsyncState } from '$lib/common/adapter/presenter/asyncState';
|
||||||
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export type AuthState = AsyncState<AuthViewModel>;
|
||||||
|
export type AuthEvent = CurrentUserLoadedEvent;
|
||||||
|
|
||||||
|
export class AuthBloc {
|
||||||
|
private readonly state = writable<AuthState>({
|
||||||
|
status: StatusType.Idle,
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly getCurrentUserUseCase: GetCurrentUserUseCase,
|
||||||
|
initialData?: AuthViewModel
|
||||||
|
) {
|
||||||
|
this.state.set({
|
||||||
|
status: StatusType.Idle,
|
||||||
|
data: initialData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get subscribe() {
|
||||||
|
return this.state.subscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispatch(event: AuthEvent): Promise<AuthState> {
|
||||||
|
switch (event.event) {
|
||||||
|
case AuthEventType.CurrentUserLoadedEvent:
|
||||||
|
return this.loadCurrentUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadCurrentUser(): Promise<AuthState> {
|
||||||
|
this.state.set({ status: StatusType.Loading, data: get(this.state).data });
|
||||||
|
|
||||||
|
const user = await this.getCurrentUserUseCase.execute();
|
||||||
|
|
||||||
|
const userViewModel = user ? UserViewModel.fromEntity(user) : null;
|
||||||
|
const authViewModel = AuthViewModel.fromEntity(userViewModel);
|
||||||
|
const result: AuthState = {
|
||||||
|
status: StatusType.Success,
|
||||||
|
data: authViewModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.state.set(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AuthEventType {
|
||||||
|
CurrentUserLoadedEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurrentUserLoadedEvent {
|
||||||
|
event: AuthEventType.CurrentUserLoadedEvent;
|
||||||
|
}
|
33
frontend/src/lib/auth/adapter/presenter/authViewModel.ts
Normal file
33
frontend/src/lib/auth/adapter/presenter/authViewModel.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { UserViewModel, type DehydratedUserProps } from '$lib/auth/adapter/presenter/userViewModel';
|
||||||
|
|
||||||
|
export class AuthViewModel {
|
||||||
|
readonly user: UserViewModel | null;
|
||||||
|
|
||||||
|
constructor(params: { user: UserViewModel | null }) {
|
||||||
|
this.user = params.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromEntity(user: UserViewModel | null): AuthViewModel {
|
||||||
|
return new AuthViewModel({ user });
|
||||||
|
}
|
||||||
|
|
||||||
|
static rehydrate(props: DehydratedAuthProps): AuthViewModel {
|
||||||
|
return new AuthViewModel({
|
||||||
|
user: props.user ? UserViewModel.rehydrate(props.user) : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dehydrate(): DehydratedAuthProps {
|
||||||
|
return {
|
||||||
|
user: this.user ? this.user.dehydrate() : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get isAuthenticated(): boolean {
|
||||||
|
return this.user !== null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DehydratedAuthProps {
|
||||||
|
user: DehydratedUserProps | null;
|
||||||
|
}
|
43
frontend/src/lib/auth/adapter/presenter/userViewModel.ts
Normal file
43
frontend/src/lib/auth/adapter/presenter/userViewModel.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { User } from '$lib/auth/domain/entity/user';
|
||||||
|
|
||||||
|
export class UserViewModel {
|
||||||
|
readonly id: number;
|
||||||
|
readonly name: string;
|
||||||
|
readonly email: string;
|
||||||
|
|
||||||
|
private constructor(props: { id: number; name: string; email: string }) {
|
||||||
|
this.id = props.id;
|
||||||
|
this.name = props.name;
|
||||||
|
this.email = props.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromEntity(user: User): UserViewModel {
|
||||||
|
return new UserViewModel({
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static rehydrate(props: DehydratedUserProps): UserViewModel {
|
||||||
|
return new UserViewModel({
|
||||||
|
id: props.id,
|
||||||
|
name: props.name,
|
||||||
|
email: props.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dehydrate(): DehydratedUserProps {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
email: this.email,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DehydratedUserProps {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import type { User } from '$lib/auth/domain/entity/user';
|
||||||
|
|
||||||
|
export interface AuthRepository {
|
||||||
|
getCurrentUser(): Promise<User | null>;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
import type { AuthRepository } from '$lib/auth/application/gateway/authRepository';
|
||||||
|
import type { User } from '$lib/auth/domain/entity/user';
|
||||||
|
|
||||||
|
export class GetCurrentUserUseCase {
|
||||||
|
constructor(private readonly authRepository: AuthRepository) {}
|
||||||
|
|
||||||
|
async execute(): Promise<User | null> {
|
||||||
|
return await this.authRepository.getCurrentUser();
|
||||||
|
}
|
||||||
|
}
|
11
frontend/src/lib/auth/domain/entity/user.ts
Normal file
11
frontend/src/lib/auth/domain/entity/user.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export class User {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
constructor(props: { id: number; name: string; email: string }) {
|
||||||
|
this.id = props.id;
|
||||||
|
this.name = props.name;
|
||||||
|
this.email = props.email;
|
||||||
|
}
|
||||||
|
}
|
20
frontend/src/lib/auth/framework/api/authApiServiceImpl.ts
Normal file
20
frontend/src/lib/auth/framework/api/authApiServiceImpl.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { AuthApiService } from '$lib/auth/adapter/gateway/authApiService';
|
||||||
|
import { UserResponseDto } from '$lib/auth/adapter/gateway/userResponseDto';
|
||||||
|
import { Environment } from '$lib/environment';
|
||||||
|
|
||||||
|
export class AuthApiServiceImpl implements AuthApiService {
|
||||||
|
constructor(private readonly fetchFn: typeof fetch) {}
|
||||||
|
|
||||||
|
async getCurrentUser(): Promise<UserResponseDto | null> {
|
||||||
|
const url = new URL('me', Environment.API_BASE_URL);
|
||||||
|
|
||||||
|
const response = await this.fetchFn(url);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = await response.json();
|
||||||
|
return UserResponseDto.fromJson(json);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
||||||
import type { PostRepository } from '$lib/post/application/repository/postRepository';
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
import type { Post } from '$lib/post/domain/entity/post';
|
import type { Post } from '$lib/post/domain/entity/post';
|
||||||
import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { PostRepository } from '$lib/post/application/repository/postRepository';
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
import type { PostInfo } from '$lib/post/domain/entity/postInfo';
|
||||||
|
|
||||||
export class GetAllPostsUseCase {
|
export class GetAllPostsUseCase {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { PostRepository } from '$lib/post/application/repository/postRepository';
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
import type { Post } from '$lib/post/domain/entity/post';
|
import type { Post } from '$lib/post/domain/entity/post';
|
||||||
|
|
||||||
export class GetPostUseCase {
|
export class GetPostUseCase {
|
||||||
|
@ -4,12 +4,12 @@ import { PostInfoResponseDto } from '$lib/post/adapter/gateway/postInfoResponseD
|
|||||||
import { PostResponseDto } from '$lib/post/adapter/gateway/postResponseDto';
|
import { PostResponseDto } from '$lib/post/adapter/gateway/postResponseDto';
|
||||||
|
|
||||||
export class PostApiServiceImpl implements PostApiService {
|
export class PostApiServiceImpl implements PostApiService {
|
||||||
constructor(private fetchFn: typeof fetch) {}
|
constructor(private readonly fetchFn: typeof fetch) {}
|
||||||
|
|
||||||
async getAllPosts(): Promise<PostInfoResponseDto[]> {
|
async getAllPosts(): Promise<PostInfoResponseDto[]> {
|
||||||
const url = new URL('post', Environment.API_BASE_URL);
|
const url = new URL('post', Environment.API_BASE_URL);
|
||||||
|
|
||||||
const response = await this.fetchFn(url.href);
|
const response = await this.fetchFn(url);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return [];
|
return [];
|
||||||
@ -22,7 +22,7 @@ export class PostApiServiceImpl implements PostApiService {
|
|||||||
async getPost(id: string): Promise<PostResponseDto | null> {
|
async getPost(id: string): Promise<PostResponseDto | null> {
|
||||||
const url = new URL(`post/${id}`, Environment.API_BASE_URL);
|
const url = new URL(`post/${id}`, Environment.API_BASE_URL);
|
||||||
|
|
||||||
const response = await this.fetchFn(url.href);
|
const response = await this.fetchFn(url);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return null;
|
return null;
|
||||||
|
40
frontend/src/routes/dashboard/+layout.svelte
Normal file
40
frontend/src/routes/dashboard/+layout.svelte
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { AuthApiService } from '$lib/auth/adapter/gateway/authApiService';
|
||||||
|
import { AuthRepositoryImpl } from '$lib/auth/adapter/gateway/authRepositoryImpl';
|
||||||
|
import { AuthBloc, AuthEventType } from '$lib/auth/adapter/presenter/authBloc';
|
||||||
|
import type { AuthRepository } from '$lib/auth/application/gateway/authRepository';
|
||||||
|
import { GetCurrentUserUseCase } from '$lib/auth/application/useCase/getCurrentUserUseCase';
|
||||||
|
import { AuthApiServiceImpl } from '$lib/auth/framework/api/authApiServiceImpl';
|
||||||
|
import { onMount, setContext } from 'svelte';
|
||||||
|
import type { LayoutProps } from './$types';
|
||||||
|
import { StatusType } from '$lib/common/adapter/presenter/asyncState';
|
||||||
|
|
||||||
|
const { children }: LayoutProps = $props();
|
||||||
|
|
||||||
|
const authApiService: AuthApiService = new AuthApiServiceImpl(fetch);
|
||||||
|
const authRepository: AuthRepository = new AuthRepositoryImpl(authApiService);
|
||||||
|
const getcurrentUserUseCase = new GetCurrentUserUseCase(authRepository);
|
||||||
|
const authBloc = new AuthBloc(getcurrentUserUseCase);
|
||||||
|
|
||||||
|
setContext(AuthBloc.name, authBloc);
|
||||||
|
|
||||||
|
const authState = $derived($authBloc);
|
||||||
|
|
||||||
|
onMount(() => authBloc.dispatch({ event: AuthEventType.CurrentUserLoadedEvent }));
|
||||||
|
|
||||||
|
const hasError = $derived.by(() => {
|
||||||
|
if (authState.status === StatusType.Error) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (authState.status === StatusType.Success && !authState.data.isAuthenticated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if hasError}
|
||||||
|
<div>Error</div>
|
||||||
|
{:else}
|
||||||
|
{@render children()}
|
||||||
|
{/if}
|
1
frontend/src/routes/dashboard/+page.svelte
Normal file
1
frontend/src/routes/dashboard/+page.svelte
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div>Dashboard</div>
|
@ -7,13 +7,15 @@
|
|||||||
import type { PageProps } from './$types';
|
import type { PageProps } from './$types';
|
||||||
import { PostInfoViewModel } from '$lib/post/adapter/presenter/postInfoViewModel';
|
import { PostInfoViewModel } from '$lib/post/adapter/presenter/postInfoViewModel';
|
||||||
import PostOverallPage from '$lib/post/framework/ui/PostOverallPage.svelte';
|
import PostOverallPage from '$lib/post/framework/ui/PostOverallPage.svelte';
|
||||||
|
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
||||||
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
const initialData = data.dehydratedData?.map((post) => PostInfoViewModel.rehydrate(post));
|
const initialData = data.dehydratedData?.map((post) => PostInfoViewModel.rehydrate(post));
|
||||||
|
|
||||||
const postApiService = new PostApiServiceImpl(fetch);
|
const postApiService: PostApiService = new PostApiServiceImpl(fetch);
|
||||||
const postRepository = new PostRepositoryImpl(postApiService);
|
const postRepository: PostRepository = new PostRepositoryImpl(postApiService);
|
||||||
const getAllPostsUseCase = new GetAllPostsUseCase(postRepository);
|
const getAllPostsUseCase = new GetAllPostsUseCase(postRepository);
|
||||||
const postListBloc = new PostListBloc(getAllPostsUseCase, initialData);
|
const postListBloc = new PostListBloc(getAllPostsUseCase, initialData);
|
||||||
|
|
||||||
|
@ -7,14 +7,16 @@
|
|||||||
import { setContext } from 'svelte';
|
import { setContext } from 'svelte';
|
||||||
import type { PageProps } from './$types';
|
import type { PageProps } from './$types';
|
||||||
import PostContentPage from '$lib/post/framework/ui/PostContentPage.svelte';
|
import PostContentPage from '$lib/post/framework/ui/PostContentPage.svelte';
|
||||||
|
import type { PostApiService } from '$lib/post/adapter/gateway/postApiService';
|
||||||
|
import type { PostRepository } from '$lib/post/application/gateway/postRepository';
|
||||||
|
|
||||||
const { data, params }: PageProps = $props();
|
const { data, params }: PageProps = $props();
|
||||||
const { id } = params;
|
const { id } = params;
|
||||||
|
|
||||||
const initialData = PostViewModel.rehydrate(data.dehydratedData!);
|
const initialData = PostViewModel.rehydrate(data.dehydratedData!);
|
||||||
|
|
||||||
const postApiService = new PostApiServiceImpl(fetch);
|
const postApiService: PostApiService = new PostApiServiceImpl(fetch);
|
||||||
const postRepository = new PostRepositoryImpl(postApiService);
|
const postRepository: PostRepository = new PostRepositoryImpl(postApiService);
|
||||||
const getPostUseCase = new GetPostUseCase(postRepository);
|
const getPostUseCase = new GetPostUseCase(postRepository);
|
||||||
const postBloc = new PostBloc(getPostUseCase, initialData);
|
const postBloc = new PostBloc(getPostUseCase, initialData);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user