Compare commits

..

No commits in common. "13a282f71f873519f95a3ec5d62c5f69aaf09ac8" and "f673db572e27d5458ebcea81b746620438411a84" have entirely different histories.

13 changed files with 21 additions and 103 deletions

View File

@ -46,13 +46,7 @@
"development": { "development": {
"optimization": false, "optimization": false,
"extractLicenses": false, "extractLicenses": false,
"sourceMap": true, "sourceMap": true
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
} }
}, },
"defaultConfiguration": "production" "defaultConfiguration": "production"

View File

@ -3,7 +3,6 @@ import { RecipePage } from './pages/recipe-page/recipe-page';
import { RecipesPage } from './pages/recipes-page/recipes-page'; import { RecipesPage } from './pages/recipes-page/recipes-page';
import { RecipesSearchPage } from './pages/recipes-search-page/recipes-search-page'; import { RecipesSearchPage } from './pages/recipes-search-page/recipes-search-page';
import { RecipeUploadPage } from './pages/recipe-upload-page/recipe-upload-page'; import { RecipeUploadPage } from './pages/recipe-upload-page/recipe-upload-page';
import { authGuard } from './shared/guards/auth-guard';
export const routes: Routes = [ export const routes: Routes = [
{ {
@ -17,7 +16,6 @@ export const routes: Routes = [
{ {
path: 'recipe-upload', path: 'recipe-upload',
component: RecipeUploadPage, component: RecipeUploadPage,
canActivate: [authGuard]
}, },
{ {
path: 'recipes/:username/:slug', path: 'recipes/:username/:slug',

View File

@ -1,6 +1,3 @@
export const Endpoints = { export const Endpoints = {
authLogin: 'auth/login',
authLogout: 'auth/logout',
authRefresh: 'auth/refresh',
recipes: 'recipes', recipes: 'recipes',
}; };

View File

@ -1,5 +0,0 @@
export interface NavLinkConfig {
relativeUrl: string;
title: string;
disabled?: boolean;
}

View File

@ -1,10 +1,8 @@
<nav> <nav>
<h2>Nav</h2> <h2>Nav</h2>
<ul> <ul>
@for (link of links(); track link.relativeUrl) { <li><a [routerLink]="'/'">Browse Recipes</a></li>
@if (!link.disabled) { <li><a [routerLink]="'/recipes-search'">Search Recipes</a></li>
<li><a [routerLink]="link.relativeUrl">{{ link.title }}</a></li> <li><a [routerLink]="'/recipe-upload'">Upload Recipe</a></li>
}
}
</ul> </ul>
</nav> </nav>

View File

@ -1,7 +1,5 @@
import { Component, computed, inject } from '@angular/core'; import { Component } from '@angular/core';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { AuthService } from '../../services/AuthService';
import { NavLinkConfig } from './NavLinkConfig';
@Component({ @Component({
selector: 'app-nav', selector: 'app-nav',
@ -9,26 +7,4 @@ import { NavLinkConfig } from './NavLinkConfig';
templateUrl: './nav.html', templateUrl: './nav.html',
styleUrl: './nav.css', styleUrl: './nav.css',
}) })
export class Nav { export class Nav {}
private readonly authService = inject(AuthService);
protected readonly links = computed<NavLinkConfig[]>(() => {
return [
{
relativeUrl: '/',
title: 'Browse Recipes',
},
{
relativeUrl: '/recipes-search',
title: 'Search Recipes',
},
{
relativeUrl: '/recipe-upload',
title: 'Upload Recipe',
disabled: this.authService.accessToken() === null
}
];
});
}

View File

@ -1,17 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { CanActivateFn } from '@angular/router';
import { authGuard } from './auth-guard';
describe('authGuardGuard', () => {
const executeGuard: CanActivateFn = (...guardParameters) =>
TestBed.runInInjectionContext(() => authGuard(...guardParameters));
beforeEach(() => {
TestBed.configureTestingModule({});
});
it('should be created', () => {
expect(executeGuard).toBeTruthy();
});
});

View File

@ -1,8 +0,0 @@
import { CanActivateFn } from '@angular/router';
import { inject } from '@angular/core';
import { AuthService } from '../services/AuthService';
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
return authService.accessToken() !== null;
};

View File

@ -4,7 +4,6 @@ import { LoginView } from '../models/LoginView.model';
import { firstValueFrom, Observable, tap } from 'rxjs'; import { firstValueFrom, Observable, tap } from 'rxjs';
import { QueryClient } from '@tanstack/angular-query-experimental'; import { QueryClient } from '@tanstack/angular-query-experimental';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { EndpointService } from './EndpointService';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -19,12 +18,11 @@ export class AuthService {
private readonly http = inject(HttpClient); private readonly http = inject(HttpClient);
private readonly queryClient = inject(QueryClient); private readonly queryClient = inject(QueryClient);
private readonly router = inject(Router); private readonly router = inject(Router);
private readonly endpointService = inject(EndpointService);
public async login(username: string, password: string): Promise<LoginView> { public async login(username: string, password: string): Promise<LoginView> {
const loginView = await firstValueFrom( const loginView = await firstValueFrom(
this.http.post<LoginView>( this.http.post<LoginView>(
this.endpointService.getUrl('authLogin'), 'http://localhost:8080/auth/login',
{ username, password }, { username, password },
{ {
withCredentials: true, withCredentials: true,
@ -38,7 +36,7 @@ export class AuthService {
} }
public async logout(): Promise<void> { public async logout(): Promise<void> {
await firstValueFrom(this.http.post(this.endpointService.getUrl('authLogout'), null)); await firstValueFrom(this.http.post('http://localhost:8080/auth/logout', null));
this._username.set(null); this._username.set(null);
this._accessToken.set(null); this._accessToken.set(null);
await this.router.navigate(['/']); await this.router.navigate(['/']);
@ -49,7 +47,7 @@ export class AuthService {
this._accessToken.set(null); this._accessToken.set(null);
this._username.set(null); this._username.set(null);
return this.http return this.http
.post<LoginView>(this.endpointService.getUrl('authRefresh'), null, { .post<LoginView>('http://localhost:8080/auth/refresh', null, {
withCredentials: true, withCredentials: true,
}) })
.pipe( .pipe(

View File

@ -1,13 +1,12 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Endpoints } from '../../endpoints'; import { Endpoints } from '../../endpoints';
import { QueryParams } from '../models/Query.model'; import { QueryParams } from '../models/Query.model';
import { environment } from '../../../environments/environment';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class EndpointService { export class EndpointService {
public getUrl(endpoint: keyof typeof Endpoints, pathParts?: string[], queryParams?: QueryParams): string { public getUrl(endpoint: keyof typeof Endpoints, pathParams?: string[], queryParams?: QueryParams): string {
const urlSearchParams = new URLSearchParams(); const urlSearchParams = new URLSearchParams();
if (queryParams?.page !== undefined) { if (queryParams?.page !== undefined) {
urlSearchParams.set('page', queryParams.page.toString()); urlSearchParams.set('page', queryParams.page.toString());
@ -30,7 +29,7 @@ export class EndpointService {
} }
}); });
let pathString = pathParts?.join('/') || ''; let pathString = pathParams?.join('/') || '';
if (pathString?.length) { if (pathString?.length) {
pathString = '/' + pathString; pathString = '/' + pathString;
} }
@ -40,6 +39,6 @@ export class EndpointService {
queryString = '?' + queryString; queryString = '?' + queryString;
} }
return environment.apiBaseUrl + '/' + Endpoints[endpoint] + pathString + queryString; return `http://localhost:8080/${Endpoints[endpoint]}${pathString}${queryString}`;
} }
} }

View File

@ -19,29 +19,24 @@ export class RecipeService {
public getRecipes(): Promise<Recipe[]> { public getRecipes(): Promise<Recipe[]> {
return firstValueFrom( return firstValueFrom(
this.http.get<RecipeInfoViews>(this.endpointService.getUrl('recipes')) this.http.get<RecipeInfoViews>('http://localhost:8080/recipes').pipe(map((res) => res.content)),
.pipe(map((res) => res.content))
); );
} }
public getRecipeView(username: string, slug: string): Promise<RecipeView> { public getRecipeView(username: string, slug: string): Promise<RecipeView> {
return firstValueFrom(this.http.get<RecipeView>( return firstValueFrom(this.http.get<RecipeView>(`http://localhost:8080/recipes/${username}/${slug}`));
this.endpointService.getUrl('recipes', [username, slug])
));
} }
private getRecipeBaseUrl(recipeView: RecipeView): string { private getRecipeUrl(recipeView: RecipeView): string {
return this.endpointService.getUrl( return `http://localhost:8080/recipes/${recipeView.recipe.owner.username}/${recipeView.recipe.slug}`;
'recipes', [recipeView.recipe.owner.username, recipeView.recipe.slug]
);
} }
public async toggleStar(recipeView: RecipeView): Promise<void> { public async toggleStar(recipeView: RecipeView): Promise<void> {
if (this.authService.accessToken()) { if (this.authService.accessToken()) {
if (recipeView.isStarred) { if (recipeView.isStarred) {
await lastValueFrom(this.http.delete(this.getRecipeBaseUrl(recipeView) + '/star')); await lastValueFrom(this.http.delete(this.getRecipeUrl(recipeView) + '/star'));
} else { } else {
await lastValueFrom(this.http.post(this.getRecipeBaseUrl(recipeView) + '/star', null)); await lastValueFrom(this.http.post(this.getRecipeUrl(recipeView) + '/star', null));
} }
await this.queryClient.invalidateQueries({ await this.queryClient.invalidateQueries({
queryKey: ['recipe', recipeView.recipe.owner.username, recipeView.recipe.slug], queryKey: ['recipe', recipeView.recipe.owner.username, recipeView.recipe.slug],
@ -61,10 +56,9 @@ export class RecipeService {
public async addComment(username: string, slug: string, commentText: string): Promise<RecipeComment> { public async addComment(username: string, slug: string, commentText: string): Promise<RecipeComment> {
const comment = await firstValueFrom( const comment = await firstValueFrom(
this.http.post<RecipeComment>( this.http.post<RecipeComment>(`http://localhost:8080/recipes/${username}/${slug}/comments`, {
this.endpointService.getUrl('recipes', [username, slug, 'comments']), text: commentText,
{ text: commentText } }),
)
); );
await this.queryClient.invalidateQueries({ await this.queryClient.invalidateQueries({
queryKey: ['recipeComments', username, slug], queryKey: ['recipeComments', username, slug],

View File

@ -1,3 +0,0 @@
export const environment = {
apiBaseUrl: 'http://localhost:8080'
};

View File

@ -1,3 +0,0 @@
export const environment = {
apiBaseUrl: 'http://localhost:8080'
};