diff --git a/backend/blog.db b/backend/blog.db index 15e0e60..0f85c74 100644 Binary files a/backend/blog.db and b/backend/blog.db differ diff --git a/backend/blog/controller.go b/backend/blog/controller.go index 820829e..adceb86 100644 --- a/backend/blog/controller.go +++ b/backend/blog/controller.go @@ -4,11 +4,13 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "git.schreifuchs.ch/schreifuchs/ng-blog/backend/model" + "github.com/gorilla/mux" ) -func (s Service) CreatePost(w http.ResponseWriter, r *http.Request) { +func (s Service) SavePost(w http.ResponseWriter, r *http.Request) { var post model.Post if err := json.NewDecoder(r.Body).Decode(&post); err != nil { w.WriteHeader(http.StatusBadRequest) @@ -37,3 +39,23 @@ func (s Service) GetAllPosts(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(&posts) w.WriteHeader(http.StatusOK) } + +func (s Service) DeletePost(w http.ResponseWriter, r *http.Request) { + idStr, ok := mux.Vars(r)["postID"] + if !ok { + w.WriteHeader(http.StatusNotFound) + return + } + id, err := strconv.Atoi(idStr) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + + err = s.db.Delete(&model.Post{}, id).Error + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, err.Error()) + } + w.WriteHeader(http.StatusNoContent) +} diff --git a/backend/main.go b/backend/main.go index 44b89b8..c6bd50b 100644 --- a/backend/main.go +++ b/backend/main.go @@ -30,7 +30,9 @@ func main() { r := mux.NewRouter() r.Use(cors.HandlerForOrigin("*")) r.Handle("/login", auth.Login(user, password, []byte(secret))).Methods("POST") - r.Handle("/posts", auth.Authenticated([]byte(secret))(blg.CreatePost)).Methods("POST") + r.Handle("/posts", auth.Authenticated([]byte(secret))(blg.SavePost)).Methods("POST") + r.Handle("/posts", auth.Authenticated([]byte(secret))(blg.SavePost)).Methods("PUT") + r.Handle("/posts/{postID}", auth.Authenticated([]byte(secret))(blg.DeletePost)).Methods("DELETE") r.Handle("/posts", http.HandlerFunc(blg.GetAllPosts)).Methods("GET") r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // The CORS middleware should set up the headers for you diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index a32cbff..4b95202 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -5,6 +5,7 @@ import { AdminComponent } from './routes/admin/admin.component'; import { LoggedInGuard } from './shared/guards/logged-in.guard'; import { PostEditorComponent } from './components/post-editor/post-editor.component'; import { CreatePostComponent } from './routes/post/create-post/create-post.component'; +import { UpdatePostComponent } from './routes/post/update-post/update-post.component'; export const routes: Routes = [ { path: '', component: HomeComponent }, @@ -12,7 +13,11 @@ export const routes: Routes = [ path: 'post', children: [ { path: 'new', component: CreatePostComponent }, - { path: ':id', component: PostComponent }, + { path: ':id/edit', component: UpdatePostComponent }, + { + path: ':id', + component: PostComponent, + }, ], }, { path: 'admin', component: AdminComponent, canActivate: [LoggedInGuard] }, diff --git a/frontend/src/app/routes/admin/admin.component.html b/frontend/src/app/routes/admin/admin.component.html index 3a12edd..29d1db0 100644 --- a/frontend/src/app/routes/admin/admin.component.html +++ b/frontend/src/app/routes/admin/admin.component.html @@ -12,9 +12,23 @@
-

{{ post.title }}

-

TL;DR; {{ post.tldr }}

+

{{ post.title }}

+

+ TL;DR; {{ post.tldr }} +

+ + Edit + +
diff --git a/frontend/src/app/routes/admin/admin.component.ts b/frontend/src/app/routes/admin/admin.component.ts index 3a0bebe..21256ee 100644 --- a/frontend/src/app/routes/admin/admin.component.ts +++ b/frontend/src/app/routes/admin/admin.component.ts @@ -11,5 +11,10 @@ import { RouterLink, RouterOutlet } from '@angular/router'; templateUrl: './admin.component.html', }) export class AdminComponent { - posts = inject(PostsService).getPosts(); + private postsService = inject(PostsService); + posts = this.postsService.getPosts(); + + delete(id: number) { + this.postsService.deletePost(id); + } } diff --git a/frontend/src/app/routes/post/update-post/update-post.component.html b/frontend/src/app/routes/post/update-post/update-post.component.html new file mode 100644 index 0000000..cebe496 --- /dev/null +++ b/frontend/src/app/routes/post/update-post/update-post.component.html @@ -0,0 +1,10 @@ +
+

Update a Post

+ +
+ diff --git a/frontend/src/app/routes/post/update-post/update-post.component.spec.ts b/frontend/src/app/routes/post/update-post/update-post.component.spec.ts new file mode 100644 index 0000000..46e22f1 --- /dev/null +++ b/frontend/src/app/routes/post/update-post/update-post.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UpdatePostComponent } from './update-post.component'; + +describe('UpdatePostComponent', () => { + let component: UpdatePostComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UpdatePostComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UpdatePostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/routes/post/update-post/update-post.component.ts b/frontend/src/app/routes/post/update-post/update-post.component.ts new file mode 100644 index 0000000..6e1b7e2 --- /dev/null +++ b/frontend/src/app/routes/post/update-post/update-post.component.ts @@ -0,0 +1,45 @@ +import { + Component, + effect, + Input, + OnInit, + signal, + WritableSignal, +} from '@angular/core'; +import { PostsService } from '../../../shared/services/posts.service'; +import { inject } from '@angular/core'; +import { Post } from '../../../shared/interfaces/post'; +import { Location } from '@angular/common'; +import { PostEditorComponent } from '../../../components/post-editor/post-editor.component'; + +@Component({ + selector: 'app-update-post', + imports: [PostEditorComponent], + templateUrl: './update-post.component.html', +}) +export class UpdatePostComponent implements OnInit { + @Input() id!: string; + private postsService = inject(PostsService); + private location = inject(Location); + post: WritableSignal = signal({ + id: 0, + title: '', + tldr: '', + content: '', + }); + + ngOnInit(): void { + const p = this.postsService.getPost(parseInt(this.id))(); + if (p == undefined) { + this.location.back(); + return; + } + + this.post.set(p); + } + + save() { + this.postsService.updatePost(this.post()); + this.location.back(); + } +} diff --git a/frontend/src/app/shared/services/posts.service.ts b/frontend/src/app/shared/services/posts.service.ts index 82f3362..4e56e17 100644 --- a/frontend/src/app/shared/services/posts.service.ts +++ b/frontend/src/app/shared/services/posts.service.ts @@ -1,7 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { computed, - effect, inject, Injectable, Signal, @@ -37,6 +36,15 @@ export class PostsService { return computed(() => this.posts().get(id)); } + deletePost(id: number) { + this.http.delete(`${environment.apiRoot}/posts/${id}`).subscribe(() => { + this.posts.update((p) => { + p.delete(id); + return new Map(p); + }); + }); + } + createPost(post: Post) { this.http .post(`${environment.apiRoot}/posts`, post) @@ -44,4 +52,11 @@ export class PostsService { this.posts.update((p) => new Map(p.set(res.id, res))); }); } + updatePost(post: Post) { + this.http + .put(`${environment.apiRoot}/posts`, post) + .subscribe((res) => { + this.posts.update((p) => new Map(p.set(res.id, res))); + }); + } }