From b0b41fcdc5eb158f138e0e04b9eb02177c0d99bb Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Tue, 11 Jun 2024 00:25:33 +0300 Subject: [PATCH] feat: add setup/cache page --- src/pages/setup/cache/cache.component.ts | 69 ++++++++++++++++++++++++ src/services/cache.service.ts | 65 ++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/pages/setup/cache/cache.component.ts create mode 100644 src/services/cache.service.ts diff --git a/src/pages/setup/cache/cache.component.ts b/src/pages/setup/cache/cache.component.ts new file mode 100644 index 0000000..9825047 --- /dev/null +++ b/src/pages/setup/cache/cache.component.ts @@ -0,0 +1,69 @@ +import {Component} from '@angular/core'; +import {NavigationService} from "@service/navigation.service"; +import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; +import SetupService from "@api/v1/setup.service"; +import {MatFormFieldModule} from "@angular/material/form-field"; +import {MatSelectModule} from "@angular/material/select"; +import {MatInput} from "@angular/material/input"; +import {MatTooltip} from "@angular/material/tooltip"; +import {MatIconButton} from "@angular/material/button"; +import {MatIcon} from "@angular/material/icon"; + +@Component({ + selector: 'app-cache', + standalone: true, + imports: [ + ReactiveFormsModule, + MatFormFieldModule, + MatSelectModule, + MatInput, + MatTooltip, + MatIconButton, + MatIcon + ], + templateUrl: './cache.component.html' +}) + +export class CacheComponent { + protected databaseForm!: FormGroup; + protected database = ''; + protected hidePass = true; + + constructor(private navigationService: NavigationService, private formBuilder: FormBuilder, private api: SetupService) { + this.databaseForm = this.formBuilder.group({ + server: ['', Validators.pattern(/^([A-Za-z0-9]+\.)+[A-Za-z]{2,}$|^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$|^([A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}$|^::1$/)], + port: ['', Validators.pattern(/^[1-9][0-9]{2,}$/)], + password: [''] + }); + + this.navigationService.setNextButtonState(false); + this.databaseForm.valueChanges.subscribe(() => { + this.navigationService.setNextButtonState(this.databaseForm.valid); + }); + } + + onDatabaseChange(selectedDatabase: string) { + this.database = selectedDatabase; + + if (selectedDatabase === 'memcached') { + this.navigationService.nextButtonAction = () => { + return this.api.setMemcached(); + }; + this.navigationService.setNextButtonState(true); + } else { + this.navigationService.nextButtonAction = () => { + return this.api.setRedis({ + "server": this.databaseForm.get('server')?.value, + "port": this.databaseForm.get('port')?.value, + "password": this.databaseForm.get('password')?.value + }); + }; + this.navigationService.setNextButtonState(this.databaseForm.valid); + } + } + + protected togglePassword(event: MouseEvent) { + this.hidePass = !this.hidePass; + event.stopPropagation(); + } +} diff --git a/src/services/cache.service.ts b/src/services/cache.service.ts new file mode 100644 index 0000000..7409523 --- /dev/null +++ b/src/services/cache.service.ts @@ -0,0 +1,65 @@ +import {Injectable} from '@angular/core'; + +interface CacheEntry { + data: T; + expiry: number; +} + +@Injectable({ + providedIn: 'root' +}) +export class CacheService { + private handleQuotaExceeded(): void { + const keys: string[] = []; + const expiries: number[] = []; + + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (!key) continue; + + const cacheEntry = JSON.parse(localStorage.getItem(key) as string) as CacheEntry; + keys.push(key); + expiries.push(cacheEntry.expiry); + } + + // Удаляем самый старый элемент + const oldestIndex = expiries.indexOf(Math.min(...expiries)); + localStorage.removeItem(keys[oldestIndex]); + } + + private isQuotaExceededError(e: unknown): e is DOMException { + return e instanceof DOMException && ( + e.name === 'QuotaExceededError' || + e.name === 'NS_ERROR_DOM_QUOTA_REACHED' + ); + } + + set(key: string, data: T, ttl: number): void { + const expiry = Date.now() + ttl; + const cacheEntry: CacheEntry = {data, expiry}; + + try { + localStorage.setItem(key, JSON.stringify(cacheEntry)); + } catch (e) { + if (this.isQuotaExceededError(e)) { + this.handleQuotaExceeded(); + localStorage.setItem(key, JSON.stringify(cacheEntry)); + } else { + throw e; + } + } + } + + get(key: string): T | null { + const cached = localStorage.getItem(key); + if (!cached) return null; + + const cacheEntry = JSON.parse(cached) as CacheEntry; + if (Date.now() > cacheEntry.expiry) { + localStorage.removeItem(key); + return null; + } + + return cacheEntry.data; + } +}