started with post editor
This commit is contained in:
@ -14,8 +14,8 @@ import (
|
|||||||
|
|
||||||
func Login(username, password string, secret []byte) http.HandlerFunc {
|
func Login(username, password string, secret []byte) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var login model.Login
|
login := model.Login{}
|
||||||
if err := json.NewDecoder(r.Response.Request.Body).Decode(&login); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&login); err != nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
BIN
backend/blog.db
BIN
backend/blog.db
Binary file not shown.
@ -5,7 +5,7 @@ import "net/http"
|
|||||||
func HandlerForOrigin(origin string) func(http.Handler) http.Handler {
|
func HandlerForOrigin(origin string) func(http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if origin := r.Header.Get("Origin"); origin != "" {
|
if o := r.Header.Get("Origin"); o != "" {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||||
@ -13,6 +13,8 @@ func HandlerForOrigin(origin string) func(http.Handler) http.Handler {
|
|||||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
}
|
}
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
@ -28,11 +28,13 @@ func main() {
|
|||||||
db := model.Init()
|
db := model.Init()
|
||||||
blg := blog.New(db)
|
blg := blog.New(db)
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
r.Use(cors.HandlerForOrigin("*"))
|
||||||
r.Handle("/login", auth.Login(user, password, []byte(secret))).Methods("POST")
|
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.CreatePost)).Methods("POST")
|
||||||
r.Handle("/posts", http.HandlerFunc(blg.GetAllPosts)).Methods("GET")
|
r.Handle("/posts", http.HandlerFunc(blg.GetAllPosts)).Methods("GET")
|
||||||
|
r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
r.Use(cors.HandlerForOrigin("*"))
|
// The CORS middleware should set up the headers for you
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
})
|
||||||
http.ListenAndServe(":8080", r)
|
http.ListenAndServe(":8080", r)
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,19 @@
|
|||||||
<a routerLink="/">
|
<a routerLink="/">
|
||||||
<h1 class="text-2xl">My Blog</h1>
|
<h1 class="text-2xl">My Blog</h1>
|
||||||
</a>
|
</a>
|
||||||
<nav></nav>
|
<nav class="flex gap-5">
|
||||||
|
<a routerLink="/admin" *ngIf="loggedIn()">admin</a>
|
||||||
|
<button *ngIf="!loggedIn()" (click)="toggleLogin()">login</button>
|
||||||
|
<button *ngIf="loggedIn()" (click)="logOut()">logout</button>
|
||||||
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<main class="w-screen h-full px-5 sm:px-20 xl:px-96 pt-5">
|
|
||||||
|
<app-modal [open]="loginOpen" (openChange)="loginOpen = $event">
|
||||||
|
<app-login />
|
||||||
|
</app-modal>
|
||||||
|
|
||||||
|
<main
|
||||||
|
class="w-screen h-full max-h-full px-5 sm:px-20 xl:px-44 3xl:px-96 pt-5 overflow-y-scroll"
|
||||||
|
>
|
||||||
<router-outlet />
|
<router-outlet />
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,11 +1,39 @@
|
|||||||
import { Component } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
computed,
|
||||||
|
effect,
|
||||||
|
inject,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges,
|
||||||
|
} from '@angular/core';
|
||||||
import { RouterLink, RouterOutlet } from '@angular/router';
|
import { RouterLink, RouterOutlet } from '@angular/router';
|
||||||
|
import { ModalComponent } from './components/modal/modal.component';
|
||||||
|
import { LoginComponent } from './components/login/login.component';
|
||||||
|
import { AuthService } from './shared/services/auth.service';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet, RouterLink],
|
imports: [RouterOutlet, RouterLink, ModalComponent, LoginComponent, NgIf],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'frontend';
|
private auth = inject(AuthService);
|
||||||
|
loginOpen: boolean = false;
|
||||||
|
loggedIn = computed(() => this.auth.jwt() !== null);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
effect(() => {
|
||||||
|
if (this.auth.jwt()) {
|
||||||
|
this.loginOpen = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleLogin() {
|
||||||
|
this.loginOpen = !this.loginOpen;
|
||||||
|
}
|
||||||
|
logOut() {
|
||||||
|
this.auth.jwt.set(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { HomeComponent } from './routes/home/home.component';
|
import { HomeComponent } from './routes/home/home.component';
|
||||||
import { PostComponent } from './routes/post/post.component';
|
import { PostComponent } from './routes/post/post.component';
|
||||||
|
import { AdminComponent } from './routes/admin/admin.component';
|
||||||
|
import { LoggedInGuard } from './shared/guards/logged-in.guard';
|
||||||
|
import { PostEditorComponent } from './components/post-editor/post-editor.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: HomeComponent },
|
{ path: '', component: HomeComponent },
|
||||||
{ path: 'post', children: [{ path: ':id', component: PostComponent }] },
|
{ path: 'post', children: [{ path: ':id', component: PostComponent }] },
|
||||||
|
{ path: 'admin', component: AdminComponent, canActivate: [LoggedInGuard] },
|
||||||
|
{ path: 'tst', component: PostEditorComponent },
|
||||||
];
|
];
|
||||||
|
22
frontend/src/app/components/login/login.component.html
Normal file
22
frontend/src/app/components/login/login.component.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<form [formGroup]="form" (ngSubmit)="submit()" class="grid grid-cols-3 gap-5">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
formControlName="name"
|
||||||
|
class="p-1 border-2 rounded-md col-span-2"
|
||||||
|
/>
|
||||||
|
<label>Password:</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
formControlName="password"
|
||||||
|
required
|
||||||
|
class="p-1 border-2 rounded-md col-span-2"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
required
|
||||||
|
class="bg-blue-300 p-2 rounded-md drop-shadow-md hover:drop-shadow-xl active:bg-blue-600"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</form>
|
23
frontend/src/app/components/login/login.component.spec.ts
Normal file
23
frontend/src/app/components/login/login.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [LoginComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
39
frontend/src/app/components/login/login.component.ts
Normal file
39
frontend/src/app/components/login/login.component.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
Validators,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormControl,
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { AuthService } from '../../shared/services/auth.service';
|
||||||
|
import { User } from '../../shared/interfaces/auth';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
imports: [ReactiveFormsModule],
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
})
|
||||||
|
export class LoginComponent {
|
||||||
|
private auth = inject(AuthService);
|
||||||
|
form = new FormGroup({
|
||||||
|
name: new FormControl('', [Validators.required]),
|
||||||
|
password: new FormControl('', [Validators.required]),
|
||||||
|
});
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.form.controls.name;
|
||||||
|
}
|
||||||
|
get password() {
|
||||||
|
return this.form.controls.password;
|
||||||
|
}
|
||||||
|
submit() {
|
||||||
|
if (this.form.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const user: User = {
|
||||||
|
name: this.form.controls.name.value!,
|
||||||
|
password: this.form.controls.password.value!,
|
||||||
|
};
|
||||||
|
this.auth.login(user);
|
||||||
|
}
|
||||||
|
}
|
9
frontend/src/app/components/modal/modal.component.html
Normal file
9
frontend/src/app/components/modal/modal.component.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<div
|
||||||
|
*ngIf="open === true"
|
||||||
|
class="fixed flex top-0 justify-center items-center w-screen h-screen z-50 backdrop-blur-md bg-black/25"
|
||||||
|
>
|
||||||
|
<div class="p-10 bg-white drop-shadow-md rounded-md">
|
||||||
|
<button (click)="toggleOpen()" class="absolute top-0 right-1 p-3">X</button>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
23
frontend/src/app/components/modal/modal.component.spec.ts
Normal file
23
frontend/src/app/components/modal/modal.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ModalComponent } from './modal.component';
|
||||||
|
|
||||||
|
describe('ModalComponent', () => {
|
||||||
|
let component: ModalComponent;
|
||||||
|
let fixture: ComponentFixture<ModalComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ModalComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ModalComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
18
frontend/src/app/components/modal/modal.component.ts
Normal file
18
frontend/src/app/components/modal/modal.component.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-modal',
|
||||||
|
imports: [NgIf],
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: './modal.component.html',
|
||||||
|
})
|
||||||
|
export class ModalComponent {
|
||||||
|
@Input() open: boolean = false;
|
||||||
|
@Output() openChange = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
toggleOpen() {
|
||||||
|
this.open = !this.open;
|
||||||
|
this.openChange.emit(this.open);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-5">
|
||||||
|
<div class="grid grid-cols-3">
|
||||||
|
<label>Title:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
[(ngModel)]="data().title"
|
||||||
|
class="p-1 border-2 rounded-md col-span-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="text-3xl">{{ data().title }}</p>
|
||||||
|
<div class="grid grid-cols-3">
|
||||||
|
<label>TL;DR;</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
[(ngModel)]="data().tldr"
|
||||||
|
class="p-1 border-2 rounded-md col-span-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="mb-5 italic text-sm">TL;DR; {{ data().tldr }}</p>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
[(ngModel)]="data().content"
|
||||||
|
class="border-2 rounded-md"
|
||||||
|
rows="20"
|
||||||
|
></textarea>
|
||||||
|
<app-markdown [markdown]="data().content" />
|
||||||
|
</div>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PostEditorComponent } from './post-editor.component';
|
||||||
|
|
||||||
|
describe('PostEditorComponent', () => {
|
||||||
|
let component: PostEditorComponent;
|
||||||
|
let fixture: ComponentFixture<PostEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [PostEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(PostEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,26 @@
|
|||||||
|
import { Component, effect, signal } from '@angular/core';
|
||||||
|
import { MarkdownComponent } from '../markdown/markdown.component';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { Post } from '../../shared/interfaces/post';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-post-editor',
|
||||||
|
imports: [MarkdownComponent, FormsModule],
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: './post-editor.component.html',
|
||||||
|
styleUrl: './post-editor.component.css',
|
||||||
|
})
|
||||||
|
export class PostEditorComponent {
|
||||||
|
data = signal<Post>({
|
||||||
|
id: 0,
|
||||||
|
title: '',
|
||||||
|
tldr: '',
|
||||||
|
content: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
effect(() => {
|
||||||
|
console.log(this.data());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
1
frontend/src/app/routes/admin/admin.component.html
Normal file
1
frontend/src/app/routes/admin/admin.component.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<app-post-editor />
|
23
frontend/src/app/routes/admin/admin.component.spec.ts
Normal file
23
frontend/src/app/routes/admin/admin.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AdminComponent } from './admin.component';
|
||||||
|
|
||||||
|
describe('AdminComponent', () => {
|
||||||
|
let component: AdminComponent;
|
||||||
|
let fixture: ComponentFixture<AdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [AdminComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(AdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
10
frontend/src/app/routes/admin/admin.component.ts
Normal file
10
frontend/src/app/routes/admin/admin.component.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { PostEditorComponent } from '../../components/post-editor/post-editor.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-admin',
|
||||||
|
imports: [PostEditorComponent],
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: './admin.component.html',
|
||||||
|
})
|
||||||
|
export class AdminComponent {}
|
@ -2,7 +2,6 @@ import { Component, inject, OnInit, Signal } from '@angular/core';
|
|||||||
import { PostsService } from '../../shared/services/posts.service';
|
import { PostsService } from '../../shared/services/posts.service';
|
||||||
import { NgForOf } from '@angular/common';
|
import { NgForOf } from '@angular/common';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
import { Post } from '../../shared/services/interfaces/post';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
|
16
frontend/src/app/shared/guards/logged-in.guard.spec.ts
Normal file
16
frontend/src/app/shared/guards/logged-in.guard.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoggedInGuard } from './logged-in.guard';
|
||||||
|
|
||||||
|
describe('LoggedInGuard', () => {
|
||||||
|
let guard: LoggedInGuard;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
guard = TestBed.inject(LoggedInGuard);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(guard).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
13
frontend/src/app/shared/guards/logged-in.guard.ts
Normal file
13
frontend/src/app/shared/guards/logged-in.guard.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { CanActivate } from '@angular/router';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class LoggedInGuard implements CanActivate {
|
||||||
|
private auth = inject(AuthService);
|
||||||
|
canActivate(): boolean {
|
||||||
|
return this.auth.jwt() !== null;
|
||||||
|
}
|
||||||
|
}
|
7
frontend/src/app/shared/interfaces/auth.ts
Normal file
7
frontend/src/app/shared/interfaces/auth.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface User {
|
||||||
|
name: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
export interface LoginResponse {
|
||||||
|
token: string;
|
||||||
|
}
|
16
frontend/src/app/shared/services/auth.service.spec.ts
Normal file
16
frontend/src/app/shared/services/auth.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
describe('AuthService', () => {
|
||||||
|
let service: AuthService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(AuthService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
20
frontend/src/app/shared/services/auth.service.ts
Normal file
20
frontend/src/app/shared/services/auth.service.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { inject, Injectable, signal, WritableSignal } from '@angular/core';
|
||||||
|
import { LoginResponse, User } from '../interfaces/auth';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthService {
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
jwt: WritableSignal<string | null> = signal(null);
|
||||||
|
|
||||||
|
login(user: User) {
|
||||||
|
console.log(user);
|
||||||
|
|
||||||
|
this.http
|
||||||
|
.post<LoginResponse>(`${environment.apiRoot}/login`, user)
|
||||||
|
.subscribe((res) => this.jwt.set(res.token));
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ import {
|
|||||||
signal,
|
signal,
|
||||||
WritableSignal,
|
WritableSignal,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Post } from './interfaces/post';
|
import { Post } from '../interfaces/post';
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
/* apply todo css */
|
/* apply todo css */
|
||||||
.todo {
|
app-markdown {
|
||||||
@layer base {
|
@layer base {
|
||||||
body {
|
body {
|
||||||
@apply bg-gray-50 text-gray-800 font-sans leading-relaxed text-base;
|
@apply bg-gray-50 text-gray-800 font-sans leading-relaxed text-base;
|
||||||
@ -30,9 +30,11 @@
|
|||||||
@apply text-blue-600 hover:text-blue-800 transition-colors underline;
|
@apply text-blue-600 hover:text-blue-800 transition-colors underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul,
|
ul {
|
||||||
|
@apply list-disc pl-6 mb-4;
|
||||||
|
}
|
||||||
ol {
|
ol {
|
||||||
@apply pl-6 mb-4;
|
@apply list-decimal pl-6 mb-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
@ -55,5 +57,11 @@
|
|||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
@apply bg-gray-50;
|
@apply bg-gray-50;
|
||||||
}
|
}
|
||||||
|
blockquote {
|
||||||
|
@apply italic bg-gray-200 rounded-md backdrop-blur-md p-3;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
@apply mb-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user