diff --git a/BetterIServ.Backend/Controllers/StorageController.cs b/BetterIServ.Backend/Controllers/StorageController.cs new file mode 100644 index 0000000..dbc4716 --- /dev/null +++ b/BetterIServ.Backend/Controllers/StorageController.cs @@ -0,0 +1,42 @@ +using System.Dynamic; +using System.Text; +using BetterIServ.Backend.Entities; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; + +namespace BetterIServ.Backend.Controllers; + +[ApiController] +[Route("storage")] +public class StorageController : ControllerBase { + + [HttpPost] + public async Task SetItem([FromQuery] string item, [FromQuery] string user) { + var data = await new StreamReader(Request.Body).ReadToEndAsync(); + var file = new FileInfo($"/data/{user}/{item}.json"); + + if (file.Directory?.Exists != true) file.Directory?.Create(); + + await using var stream = file.Exists ? file.OpenWrite() : file.Create(); + await stream.WriteAsync(Encoding.UTF8.GetBytes(data)); + return Ok(); + } + + [HttpGet] + public async Task>> GetItem([FromQuery] string item, [FromQuery] string user) { + var file = new FileInfo($"/data/{user}/{item}.json"); + if (!file.Exists) return NotFound(); + + await using var stream = file.OpenRead(); + var data = await new StreamReader(stream).ReadToEndAsync(); + return Ok(new SingleResult{Value = data}); + } + + [HttpDelete] + public IActionResult Clear([FromQuery] string user) { + if (!Directory.Exists($"/data/{user}")) return NotFound(); + Directory.Delete($"/data/{user}", true); + return Ok(); + } + +} \ No newline at end of file diff --git a/BetterIServ.Mobile/src/app/api/iserv.service.ts b/BetterIServ.Mobile/src/app/api/iserv.service.ts index 3806f91..91ae8fb 100644 --- a/BetterIServ.Mobile/src/app/api/iserv.service.ts +++ b/BetterIServ.Mobile/src/app/api/iserv.service.ts @@ -4,16 +4,21 @@ import {Userdata, AuthKeys} from "../entities/userdata"; import {firstValueFrom} from "rxjs"; import {environment} from "../../environments/environment"; import {Course} from "../entities/course"; +import {StorageService} from "./storage.service"; @Injectable({ providedIn: 'root', }) export class IServService { - public userdata?: Userdata; + public static userdata?: Userdata; public keys?: AuthKeys; public backend: string = environment.backend; + public get userdata(): Userdata { + return IServService.userdata; + } + public courseNames: {[id: string]: string} = { ["Bi"]: "Biologie", ["Ch"]: "Chemie", @@ -43,10 +48,10 @@ export class IServService { {name: "Rot", val: "danger"} ]; - constructor(private client: HttpClient) { + constructor(private client: HttpClient, private storage: StorageService) { const data = localStorage.getItem("userdata"); if (data != null) { - this.userdata = JSON.parse(data); + IServService.userdata = JSON.parse(data); } const keys = localStorage.getItem("keys"); @@ -57,15 +62,15 @@ export class IServService { public async login(email: string, password: string): Promise { const split = email.split('@'); - this.userdata = { + IServService.userdata = { username: split[0], domain: split[1], password }; try { - const keys = await firstValueFrom(this.client.post(this.backend + "/iserv/login", this.userdata)); - localStorage.setItem("userdata", JSON.stringify(this.userdata)); + const keys = await firstValueFrom(this.client.post(this.backend + "/iserv/login", IServService.userdata)); + localStorage.setItem("userdata", JSON.stringify(IServService.userdata)); localStorage.setItem("keys", JSON.stringify(keys)); return true; }catch (error) { @@ -74,31 +79,29 @@ export class IServService { } public logout() { - delete this.userdata; + delete IServService.userdata; delete this.keys; } public async getKeys(): Promise { - const keys = await firstValueFrom(this.client.post(this.backend + "/iserv/login", this.userdata)); + const keys = await firstValueFrom(this.client.post(this.backend + "/iserv/login", IServService.userdata)); localStorage.setItem("keys", JSON.stringify(keys)); return keys; } public async getGroups(): Promise { try { - return (await firstValueFrom(this.client.post<{value: string[]}>(this.backend + "/iserv/groups?domain=" + this.userdata.domain, this.keys))).value; + return (await firstValueFrom(this.client.post<{value: string[]}>(this.backend + "/iserv/groups?domain=" + IServService.userdata.domain, this.keys))).value; } catch { const keys = await this.getKeys(); - return (await firstValueFrom(this.client.post<{value: string[]}>(this.backend + "/iserv/groups?domain=" + this.userdata.domain, keys))).value; + return (await firstValueFrom(this.client.post<{value: string[]}>(this.backend + "/iserv/groups?domain=" + IServService.userdata.domain, keys))).value; } } public async getCoursesAndClass(groups?: string[]): Promise<{class: string, courses: Course[]}> { - if (localStorage.getItem("courses") && localStorage.getItem("class")) { - const courses = JSON.parse(localStorage.getItem("courses")) as Course[]; - const className = localStorage.getItem("class"); - return {courses, class: className}; - } + const courses = await this.storage.getItem("courses"); + const className = await this.storage.getItem("class", false); + if (courses != undefined && className != undefined) return {class: className, courses}; if (groups == undefined) { groups = await this.getGroups(); @@ -115,7 +118,7 @@ export class IServService { result.class = grades[0].replace("Jahrgang ", "").toUpperCase(); } } - localStorage.setItem("class", result.class); + await this.storage.setItem("class", result.class); for (let group of groups) { if (!group.includes(".") || !group.toLowerCase().startsWith("q")) continue; @@ -141,7 +144,7 @@ export class IServService { return 0; }); - localStorage.setItem("courses", JSON.stringify(courses)); + await this.storage.setItem("courses", JSON.stringify(courses)); return {class: result.class, courses}; } diff --git a/BetterIServ.Mobile/src/app/api/storage.service.ts b/BetterIServ.Mobile/src/app/api/storage.service.ts new file mode 100644 index 0000000..0034df1 --- /dev/null +++ b/BetterIServ.Mobile/src/app/api/storage.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {firstValueFrom} from "rxjs"; +import {environment} from "../../environments/environment"; +import {IServService} from "./iserv.service"; + +@Injectable({ + providedIn: 'root' +}) +export class StorageService { + + constructor(private client: HttpClient) {} + + public async getItem(item: string, isJson: boolean = true): Promise { + try { + const data = await firstValueFrom(this.client.get<{value: string}>(environment.backend + `/storage?user=${IServService.userdata.username}&item=${item}`)); + + if (isJson) return JSON.parse(data.value) as T; + return data.value as T; + }catch { + return undefined; + } + } + + public async setItem(item: string, value: any) { + await firstValueFrom(this.client.post(environment.backend + `/storage?user=${IServService.userdata.username}&item=${item}`, value)); + } + + public async clear() { + await firstValueFrom(this.client.delete(environment.backend + `/storage?user=${IServService.userdata.username}`)); + } + +} diff --git a/BetterIServ.Mobile/src/app/app.component.ts b/BetterIServ.Mobile/src/app/app.component.ts index 32c561f..3592e5b 100644 --- a/BetterIServ.Mobile/src/app/app.component.ts +++ b/BetterIServ.Mobile/src/app/app.component.ts @@ -3,6 +3,7 @@ import { Component } from '@angular/core'; import {Router, RouterLink, RouterLinkActive} from '@angular/router'; import { IonicModule } from '@ionic/angular'; import {IServService} from "./api/iserv.service"; +import {StorageService} from "./api/storage.service"; @Component({ selector: 'app-root', templateUrl: 'app.component.html', @@ -20,16 +21,16 @@ export class AppComponent { { title: 'Vertretungsplan', url: '/substitution', icon: 'list' }, ]; - constructor(public router: Router, public iserv: IServService) { + constructor(public router: Router, public iserv: IServService, private storage: StorageService) { if (localStorage.getItem("userdata") == null) { this.router.navigate(["login"]); } } - public logout() { - localStorage.clear(); + public async logout() { + await this.storage.clear(); this.iserv.logout(); - this.router.navigate(["login"]); + await this.router.navigate(["login"]); } } diff --git a/BetterIServ.Mobile/src/app/pages/home/home.page.html b/BetterIServ.Mobile/src/app/pages/home/home.page.html index 8fecdb3..843dd81 100644 --- a/BetterIServ.Mobile/src/app/pages/home/home.page.html +++ b/BetterIServ.Mobile/src/app/pages/home/home.page.html @@ -30,7 +30,7 @@ Stundenplan - + Kein Unterricht @@ -48,7 +48,7 @@ {{subsDate?.toLocaleDateString()}} - + Keine Vertretungen @@ -64,7 +64,7 @@ - + Keine ungelesenen E-Mails diff --git a/BetterIServ.Mobile/src/app/pages/home/home.page.ts b/BetterIServ.Mobile/src/app/pages/home/home.page.ts index e5ba027..cc6708c 100644 --- a/BetterIServ.Mobile/src/app/pages/home/home.page.ts +++ b/BetterIServ.Mobile/src/app/pages/home/home.page.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import { CommonModule } from '@angular/common'; +import {CommonModule, Time} from '@angular/common'; import { FormsModule } from '@angular/forms'; import { IonicModule } from '@ionic/angular'; import {IServService} from "../../api/iserv.service"; @@ -12,6 +12,7 @@ import {MailComponent} from "../../components/mail/mail.component"; import {Router} from "@angular/router"; import {Course, Lesson, Timetable} from "../../entities/course"; import {LessonComponent} from "../../components/lesson/lesson.component"; +import {StorageService} from "../../api/storage.service"; @Component({ selector: 'app-home', @@ -30,7 +31,7 @@ export class HomePage implements OnInit { public classData: {class: string, courses: Course[]}; public lessons: Lesson[]; - public constructor(public iserv: IServService, public mails: MailService, public units: UnitsService, public router: Router) {} + public constructor(public iserv: IServService, public mails: MailService, public units: UnitsService, public router: Router, private storage: StorageService) {} async ngOnInit() { this.today = new Date(); @@ -40,11 +41,17 @@ export class HomePage implements OnInit { const mailPromise = this.mails.getMails("INBOX", 0); const classPromise = this.iserv.getCoursesAndClass(); const subsPromise = this.units.getSubstitutionPlan("today"); - await Promise.all([mailPromise, classPromise, subsPromise]); + const timetablePromise = this.storage.getItem("timetable"); + await Promise.all([mailPromise, classPromise, subsPromise, timetablePromise]); this.unreadMails = (await mailPromise).filter(mail => !mail.read); this.classData = await classPromise; let unitsData = await subsPromise; + const timetable = await timetablePromise; + + if (scheduleDay != undefined && timetable != undefined) { + this.lessons = timetable[scheduleDay].filter(lesson => lesson != undefined); + } if (this.dateIsPast(unitsData.date, new Date())) { unitsData = await this.units.getSubstitutionPlan("tomorrow"); @@ -55,10 +62,6 @@ export class HomePage implements OnInit { if (this.classData.class.startsWith("Q")) { this.subs = this.subs.filter(subs => this.classData.courses.filter(course => course.id == subs.lesson).length > 0); } - - if (scheduleDay != undefined && localStorage.getItem("timetable")) { - this.lessons = (JSON.parse(localStorage.getItem("timetable")) as Timetable)[scheduleDay].filter(lesson => lesson != undefined); - } } private dateIsPast(first: Date, second: Date): boolean { diff --git a/BetterIServ.Mobile/src/app/pages/schedule/schedule.page.ts b/BetterIServ.Mobile/src/app/pages/schedule/schedule.page.ts index ec78eee..d528a74 100644 --- a/BetterIServ.Mobile/src/app/pages/schedule/schedule.page.ts +++ b/BetterIServ.Mobile/src/app/pages/schedule/schedule.page.ts @@ -6,6 +6,7 @@ import {IServService} from "../../api/iserv.service"; import {Course, Lesson, Timetable} from "../../entities/course"; import {WeekPipe} from "../../pipes/week.pipe"; import {LessonComponent} from "../../components/lesson/lesson.component"; +import {StorageService} from "../../api/storage.service"; @Component({ selector: 'app-schedule', @@ -26,20 +27,19 @@ export class SchedulePage implements OnInit { @ViewChild('courseModal') courseModal: IonModal; @ViewChild('tableModal') tableModal: IonModal; - constructor(public iserv: IServService) { } + constructor(public iserv: IServService, private storage: StorageService) { } async ngOnInit() { this.courses = (await this.iserv.getCoursesAndClass()).courses; + this.timetable = await this.storage.getItem("timetable"); - if (localStorage.getItem("timetable") == undefined) { + if (this.timetable == undefined) { for (let day of ['mon', 'tue', 'wed', 'thu', 'fri']) { for (let i = 0; i < 10; i++) { this.timetable[day].push(undefined); } } - localStorage.setItem("timetable", JSON.stringify(this.timetable)); - }else { - this.timetable = JSON.parse(localStorage.getItem("timetable")); + await this.storage.setItem("timetable", this.timetable); } } @@ -71,7 +71,7 @@ export class SchedulePage implements OnInit { if (a.name > b.name) return 1; return 0; }); - localStorage.setItem("courses", JSON.stringify(this.courses)); + await this.storage.setItem("courses", JSON.stringify(this.courses)); } public async updateOrCreateLesson(event: any) { @@ -88,7 +88,7 @@ export class SchedulePage implements OnInit { this.timetable[data.day][data.time] = data.lesson; } - localStorage.setItem("timetable", JSON.stringify(this.timetable)); + await this.storage.setItem("timetable", JSON.stringify(this.timetable)); location.reload(); } @@ -120,11 +120,11 @@ export class SchedulePage implements OnInit { this.allCourses.push({short, name, id: short, color: this.getRandomColor()}); } - public saveCourses(event: any) { + public async saveCourses(event: any) { if (event.detail.role != "confirm") return; this.courses = this.allCourses; delete this.allCourses; - localStorage.setItem("courses", JSON.stringify(this.courses)); + await this.storage.setItem("courses", this.courses); } } diff --git a/BetterIServ.Mobile/src/app/pages/substitution/substitution.page.ts b/BetterIServ.Mobile/src/app/pages/substitution/substitution.page.ts index 3f6adcb..191828a 100644 --- a/BetterIServ.Mobile/src/app/pages/substitution/substitution.page.ts +++ b/BetterIServ.Mobile/src/app/pages/substitution/substitution.page.ts @@ -6,6 +6,7 @@ import {UnitsService} from "../../api/units.service"; import {UnitsData} from "../../entities/substitution"; import {IServService} from "../../api/iserv.service"; import {SubstitutionComponent} from "../../components/substitution/substitution.component"; +import {StorageService} from "../../api/storage.service"; @Component({ selector: 'app-substitution', @@ -22,12 +23,12 @@ export class SubstitutionPage implements OnInit { public currentClass: string; public filterByClasses: boolean = false; - constructor(public units: UnitsService, private iserv: IServService, private alerts: AlertController) { - this.currentClass = localStorage.getItem("class") || 'all'; - this.filterByClasses = localStorage.getItem("filterByClasses") == "true"; - } + constructor(public units: UnitsService, private iserv: IServService, private alerts: AlertController, private storage: StorageService) {} async ngOnInit() { + this.currentClass = await this.storage.getItem("class", false) || 'all'; + this.filterByClasses = await this.storage.getItem("filterByClasses", false) == "true"; + if (!this.units.doesSchoolExist()) { const alert = await this.alerts.create({ subHeader: "Fehler", @@ -47,7 +48,7 @@ export class SubstitutionPage implements OnInit { this.courses.push(course.id); } - if (localStorage.getItem("filterByClasses") == null) { + if (await this.storage.getItem("filterByClasses") == undefined) { this.showOnlyCourses(true); this.filterByClasses = true; @@ -69,14 +70,14 @@ export class SubstitutionPage implements OnInit { public changeClass(className: string) { this.currentClass = className; - localStorage.setItem("class", className); + this.storage.setItem("class", className); this.filterByClasses = false; this.showOnlyCourses(false); } public showOnlyCourses(toggle: boolean) { - localStorage.setItem("filterByClasses", toggle.toString()); + this.storage.setItem("filterByClasses", toggle.toString()); } public hasClass(course: string): boolean { diff --git a/BetterIServ.Mobile/src/app/pipes/week.pipe.ts b/BetterIServ.Mobile/src/app/pipes/week.pipe.ts index a779783..25ee661 100644 --- a/BetterIServ.Mobile/src/app/pipes/week.pipe.ts +++ b/BetterIServ.Mobile/src/app/pipes/week.pipe.ts @@ -8,6 +8,8 @@ import {Lesson} from "../entities/course"; export class WeekPipe implements PipeTransform { transform(objects: Lesson[]): Lesson[] { + if (objects == undefined) return []; + const week = this.getWeek(new Date()) % 2; const label = week == 0 ? "a" : "b"; const result = [];