Compare commits
2 commits
0c01237456
...
55081b24d8
Author | SHA1 | Date | |
---|---|---|---|
55081b24d8 | |||
d8fe5dc9f4 |
29 changed files with 264 additions and 298 deletions
|
@ -34,7 +34,7 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
import { advent22Store } from "./plugins/store";
|
||||
import { advent22Store } from "./lib/store";
|
||||
|
||||
import AdminView from "./components/admin/AdminView.vue";
|
||||
import AdminButton from "./components/AdminButton.vue";
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Credentials } from "@/lib/api";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Credentials } from "@/lib/model";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import BulmaButton from "./bulma/Button.vue";
|
||||
|
|
|
@ -46,8 +46,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Door } from "@/lib/door";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import MultiModal from "./MultiModal.vue";
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import BulmaButton from "./bulma/Button.vue";
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import Calendar from "./Calendar.vue";
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { NumStrDict, objForEach } from "@/lib/api";
|
||||
import { objForEach } from "@/lib/helpers";
|
||||
import { NumStrDict } from "@/lib/model";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import MultiModal from "../MultiModal.vue";
|
||||
|
|
|
@ -184,8 +184,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { AdminConfigModel, Credentials, DoorSaved } from "@/lib/api";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { AdminConfigModel, Credentials, DoorSaved } from "@/lib/model";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { DateTime } from "luxon";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
|
|
|
@ -69,9 +69,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { DoorSaved } from "@/lib/api";
|
||||
import { Door } from "@/lib/door";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { DoorSaved } from "@/lib/model";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import { toast } from "bulma-toast";
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Door } from "@/lib/door";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import SVGRect from "./SVGRect.vue";
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Rectangle } from "@/lib/rectangle";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Rectangle } from "@/lib/rects/rectangle";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
type BulmaVariant =
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vector2D } from "@/lib/vector2d";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Vector2D } from "@/lib/rects/vector2d";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
function get_event_thous(event: MouseEvent): Vector2D {
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Door } from "@/lib/door";
|
||||
import { Rectangle } from "@/lib/rectangle";
|
||||
import { Vector2D } from "@/lib/vector2d";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { Rectangle } from "@/lib/rects/rectangle";
|
||||
import { Vector2D } from "@/lib/rects/vector2d";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import CalendarDoor from "../calendar/CalendarDoor.vue";
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Door } from "@/lib/door";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import ThouCanvas from "../calendar/ThouCanvas.vue";
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Door } from "@/lib/door";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import DoorCanvas from "./DoorCanvas.vue";
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Door } from "@/lib/door";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import SVGRect from "../calendar/SVGRect.vue";
|
||||
|
|
10
ui/src/d.ts/shims-advent22.d.ts
vendored
10
ui/src/d.ts/shims-advent22.d.ts
vendored
|
@ -1,10 +0,0 @@
|
|||
import { Advent22 } from "@/plugins/advent22";
|
||||
|
||||
declare module "@vue/runtime-core" {
|
||||
// bind to `this` keyword
|
||||
interface ComponentCustomProperties {
|
||||
$advent22: Advent22;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -1,71 +1,82 @@
|
|||
export interface AdminConfigModel {
|
||||
solution: {
|
||||
value: string;
|
||||
whitespace: string;
|
||||
special_chars: string;
|
||||
case: string;
|
||||
clean: string;
|
||||
};
|
||||
puzzle: {
|
||||
first: string;
|
||||
next: string | null;
|
||||
last: string;
|
||||
end: string;
|
||||
seed: string;
|
||||
extra_days: number[];
|
||||
skip_empty: boolean;
|
||||
};
|
||||
calendar: {
|
||||
config_file: string;
|
||||
background: string;
|
||||
favicon: string;
|
||||
};
|
||||
image: {
|
||||
size: number;
|
||||
border: number;
|
||||
};
|
||||
fonts: { file: string; size: number }[];
|
||||
redis: {
|
||||
host: string;
|
||||
port: number;
|
||||
db: number;
|
||||
protocol: number;
|
||||
};
|
||||
webdav: {
|
||||
url: string;
|
||||
cache_ttl: number;
|
||||
config_file: string;
|
||||
import axios, {
|
||||
AxiosBasicCredentials,
|
||||
type AxiosRequestConfig,
|
||||
type Method,
|
||||
type RawAxiosRequestHeaders,
|
||||
} from "axios";
|
||||
import { APIError } from "./api_error";
|
||||
|
||||
interface Params {
|
||||
endpoint: string;
|
||||
method?: Method;
|
||||
data?: unknown;
|
||||
headers?: RawAxiosRequestHeaders;
|
||||
config?: AxiosRequestConfig;
|
||||
}
|
||||
|
||||
export class API {
|
||||
private static get api_baseurl(): string {
|
||||
// in production mode, return "proto://hostname/api"
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
return `${window.location.protocol}//${window.location.host}/api`;
|
||||
} else if (process.env.NODE_ENV !== "development") {
|
||||
// not in prouction or development mode
|
||||
console.warn("Unexpected NODE_ENV value");
|
||||
}
|
||||
|
||||
// in development mode, return "proto://hostname:8000/api"
|
||||
return `${window.location.protocol}//${window.location.hostname}:8000/api`;
|
||||
}
|
||||
|
||||
private static readonly axios = axios.create({
|
||||
timeout: 10e3,
|
||||
baseURL: this.api_baseurl,
|
||||
});
|
||||
|
||||
private static readonly storage_key = "advent22/credentials";
|
||||
|
||||
public static set creds(value: AxiosBasicCredentials | null) {
|
||||
if (value === null) {
|
||||
localStorage.removeItem(this.storage_key);
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(this.storage_key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
public static get creds(): AxiosBasicCredentials | undefined {
|
||||
const auth_json = localStorage.getItem(this.storage_key);
|
||||
if (auth_json !== null) return JSON.parse(auth_json);
|
||||
}
|
||||
|
||||
private static get_axios_config({
|
||||
endpoint,
|
||||
method = "GET",
|
||||
data,
|
||||
headers = {},
|
||||
config = {},
|
||||
}: Params): AxiosRequestConfig {
|
||||
return {
|
||||
url: endpoint,
|
||||
method: method,
|
||||
data: data,
|
||||
auth: this.creds,
|
||||
headers: headers,
|
||||
...config,
|
||||
};
|
||||
}
|
||||
|
||||
export interface SiteConfigModel {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
content: string;
|
||||
footer: string;
|
||||
}
|
||||
public static async request<T = string>(p: Params): Promise<T>;
|
||||
public static async request<T = string>(p: string): Promise<T>;
|
||||
public static async request<T = string>(p: Params | string): Promise<T> {
|
||||
if (typeof p === "string") p = { endpoint: p };
|
||||
|
||||
export interface NumStrDict {
|
||||
[key: number]: string;
|
||||
}
|
||||
|
||||
export interface DoorSaved {
|
||||
day: number;
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export type Credentials = [username: string, password: string];
|
||||
|
||||
export function objForEach<T>(
|
||||
obj: T,
|
||||
f: (k: keyof T, v: T[keyof T]) => void,
|
||||
): void {
|
||||
for (const k in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, k)) {
|
||||
f(k, obj[k]);
|
||||
try {
|
||||
const response = await this.axios.request<T>(this.get_axios_config(p));
|
||||
return response.data;
|
||||
} catch (reason) {
|
||||
console.error(`Failed to query ${p.endpoint}: ${reason}`);
|
||||
throw new APIError(reason, p.endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
73
ui/src/lib/api_error.ts
Normal file
73
ui/src/lib/api_error.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { AxiosError } from "axios";
|
||||
import { toast } from "bulma-toast";
|
||||
|
||||
export class APIError extends Error {
|
||||
reason: unknown;
|
||||
axios_error: AxiosError | null = null;
|
||||
|
||||
constructor(reason: unknown, endpoint: string) {
|
||||
super(endpoint); // sets this.message to the endpoint
|
||||
this.reason = reason;
|
||||
Object.setPrototypeOf(this, APIError.prototype);
|
||||
|
||||
if (reason instanceof AxiosError) {
|
||||
this.axios_error = reason;
|
||||
}
|
||||
}
|
||||
|
||||
public format(): string {
|
||||
let msg =
|
||||
"Unbekannter Fehler, bitte wiederholen! Besteht das Problem länger, bitte Admin benachrichtigen!";
|
||||
let code = "U";
|
||||
const result = () => `${msg} (Fehlercode: ${code}/${this.message})`;
|
||||
|
||||
if (this.axios_error === null) return result();
|
||||
|
||||
switch (this.axios_error.code) {
|
||||
case "ECONNABORTED":
|
||||
// API unerreichbar
|
||||
msg =
|
||||
"API antwortet nicht, bitte später wiederholen! Besteht das Problem länger, bitte Admin benachrichtigen!";
|
||||
code = "D";
|
||||
break;
|
||||
|
||||
case "ERR_NETWORK":
|
||||
// Netzwerk nicht verbunden
|
||||
msg = "Sieht aus, als sei deine Netzwerkverbindung gestört.";
|
||||
code = "N";
|
||||
break;
|
||||
|
||||
default:
|
||||
if (this.axios_error.response === undefined) return result();
|
||||
|
||||
switch (this.axios_error.response.status) {
|
||||
case 401:
|
||||
// UNAUTHORIZED
|
||||
msg = "Netter Versuch :)";
|
||||
code = "A";
|
||||
break;
|
||||
|
||||
case 422:
|
||||
// UNPROCESSABLE ENTITY
|
||||
msg = "Funktion ist kaputt, bitte Admin benachrichtigen!";
|
||||
code = "I";
|
||||
break;
|
||||
|
||||
default:
|
||||
// HTTP
|
||||
code = `H${this.axios_error.response.status}`;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result();
|
||||
}
|
||||
|
||||
public alert() {
|
||||
toast({
|
||||
message: this.format(),
|
||||
type: "is-danger",
|
||||
});
|
||||
}
|
||||
}
|
10
ui/src/lib/helpers.ts
Normal file
10
ui/src/lib/helpers.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export function objForEach<T>(
|
||||
obj: T,
|
||||
f: (k: keyof T, v: T[keyof T]) => void,
|
||||
): void {
|
||||
for (const k in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, k)) {
|
||||
f(k, obj[k]);
|
||||
}
|
||||
}
|
||||
}
|
67
ui/src/lib/model.ts
Normal file
67
ui/src/lib/model.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
export interface AdminConfigModel {
|
||||
solution: {
|
||||
value: string;
|
||||
whitespace: string;
|
||||
special_chars: string;
|
||||
case: string;
|
||||
clean: string;
|
||||
};
|
||||
puzzle: {
|
||||
first: string;
|
||||
next: string | null;
|
||||
last: string;
|
||||
end: string;
|
||||
seed: string;
|
||||
extra_days: number[];
|
||||
skip_empty: boolean;
|
||||
};
|
||||
calendar: {
|
||||
config_file: string;
|
||||
background: string;
|
||||
favicon: string;
|
||||
};
|
||||
image: {
|
||||
size: number;
|
||||
border: number;
|
||||
};
|
||||
fonts: { file: string; size: number }[];
|
||||
redis: {
|
||||
host: string;
|
||||
port: number;
|
||||
db: number;
|
||||
protocol: number;
|
||||
};
|
||||
webdav: {
|
||||
url: string;
|
||||
cache_ttl: number;
|
||||
config_file: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SiteConfigModel {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
content: string;
|
||||
footer: string;
|
||||
}
|
||||
|
||||
export interface NumStrDict {
|
||||
[key: number]: string;
|
||||
}
|
||||
|
||||
export interface DoorSaved {
|
||||
day: number;
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export interface ImageData {
|
||||
height: number;
|
||||
width: number;
|
||||
aspect_ratio: number;
|
||||
data_url: string;
|
||||
}
|
||||
|
||||
export type Credentials = [username: string, password: string];
|
|
@ -1,4 +1,4 @@
|
|||
import { DoorSaved } from "./api";
|
||||
import { DoorSaved } from "../model";
|
||||
import { Rectangle } from "./rectangle";
|
||||
import { Vector2D } from "./vector2d";
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
import { Credentials, DoorSaved, SiteConfigModel } from "@/lib/api";
|
||||
import { Door } from "@/lib/door";
|
||||
import { Advent22 } from "@/plugins/advent22";
|
||||
import { RemovableRef, useLocalStorage } from "@vueuse/core";
|
||||
import { AxiosBasicCredentials, AxiosError } from "axios";
|
||||
import { toast } from "bulma-toast";
|
||||
import { acceptHMRUpdate, defineStore } from "pinia";
|
||||
import { API } from "./api";
|
||||
import { Credentials, DoorSaved, SiteConfigModel } from "./model";
|
||||
import { Door } from "./rects/door";
|
||||
|
||||
declare global {
|
||||
interface Navigator {
|
||||
|
@ -13,9 +10,6 @@ declare global {
|
|||
}
|
||||
|
||||
type State = {
|
||||
advent22: Advent22;
|
||||
api_creds: RemovableRef<Credentials>;
|
||||
is_initialized: boolean;
|
||||
on_initialized: (() => void)[];
|
||||
is_touch_device: boolean;
|
||||
is_admin: boolean;
|
||||
|
@ -30,9 +24,6 @@ export const advent22Store = defineStore({
|
|||
id: "advent22",
|
||||
|
||||
state: (): State => ({
|
||||
advent22: new Advent22(),
|
||||
api_creds: useLocalStorage("advent22/auth", ["", ""]),
|
||||
is_initialized: false,
|
||||
on_initialized: [],
|
||||
is_touch_device:
|
||||
window.matchMedia("(any-hover: none)").matches ||
|
||||
|
@ -52,81 +43,14 @@ export const advent22Store = defineStore({
|
|||
next_door_target: null,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
axios_creds: (state): AxiosBasicCredentials => {
|
||||
const [username, password] = state.api_creds;
|
||||
return { username: username, password: password };
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
init(): void {
|
||||
this.update()
|
||||
.then(() => {
|
||||
this.is_initialized = true;
|
||||
for (const callback of this.on_initialized) callback();
|
||||
})
|
||||
.catch(this.alert_user_error);
|
||||
},
|
||||
|
||||
format_user_error([reason, endpoint]: [unknown, string]): string {
|
||||
let msg =
|
||||
"Unbekannter Fehler, bitte wiederholen! Besteht das Problem länger, bitte Admin benachrichtigen!";
|
||||
let code = "U";
|
||||
const result = () => `${msg} (Fehlercode: ${code}/${endpoint})`;
|
||||
|
||||
if (!(reason instanceof AxiosError)) return result();
|
||||
|
||||
switch (reason.code) {
|
||||
case "ECONNABORTED":
|
||||
// API unerreichbar
|
||||
msg =
|
||||
"API antwortet nicht, bitte später wiederholen! Besteht das Problem länger, bitte Admin benachrichtigen!";
|
||||
code = "D";
|
||||
break;
|
||||
|
||||
case "ERR_NETWORK":
|
||||
// Netzwerk nicht verbunden
|
||||
msg = "Sieht aus, als sei deine Netzwerkverbindung gestört.";
|
||||
code = "N";
|
||||
break;
|
||||
|
||||
default:
|
||||
if (reason.response === undefined) return result();
|
||||
|
||||
switch (reason.response.status) {
|
||||
case 401:
|
||||
// UNAUTHORIZED
|
||||
msg = "Netter Versuch :)";
|
||||
code = "A";
|
||||
break;
|
||||
|
||||
case 422:
|
||||
// UNPROCESSABLE ENTITY
|
||||
msg = "Funktion ist kaputt, bitte Admin benachrichtigen!";
|
||||
code = "I";
|
||||
break;
|
||||
|
||||
default:
|
||||
// HTTP
|
||||
code = `H${reason.response.status}`;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result();
|
||||
},
|
||||
|
||||
alert_user_error(param: [unknown, string]): void {
|
||||
toast({
|
||||
message: this.format_user_error(param),
|
||||
type: "is-danger",
|
||||
});
|
||||
this.update().then(() => this.on_initialized.forEach((fn) => fn()));
|
||||
},
|
||||
|
||||
update(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
API.request("user/favicon");
|
||||
this.advent22
|
||||
.api_get_blob("user/favicon")
|
||||
.then((favicon_src) => {
|
||||
|
@ -204,7 +128,7 @@ export const advent22Store = defineStore({
|
|||
},
|
||||
|
||||
login(creds: Credentials): Promise<boolean> {
|
||||
this.api_creds = creds;
|
||||
API.creds = { username: creds[0], password: creds[1] };
|
||||
return this.update_is_admin();
|
||||
},
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { advent22Store } from "@/lib/store";
|
||||
import { Advent22Plugin } from "@/plugins/advent22";
|
||||
import { FontAwesomePlugin } from "@/plugins/fontawesome";
|
||||
import { advent22Store } from "@/plugins/store";
|
||||
import * as bulmaToast from "bulma-toast";
|
||||
import { createPinia } from "pinia";
|
||||
import { createApp } from "vue";
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
import axios, { AxiosInstance, ResponseType } from "axios";
|
||||
import { App, Plugin } from "vue";
|
||||
import { advent22Store } from "./store";
|
||||
|
||||
export class Advent22 {
|
||||
private axios: AxiosInstance;
|
||||
|
||||
public constructor() {
|
||||
this.axios = axios.create({
|
||||
timeout: 10e3,
|
||||
});
|
||||
}
|
||||
|
||||
private get api_baseurl(): string {
|
||||
// in production mode, return "//host/api"
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
return `//${window.location.host}/api`;
|
||||
} else if (process.env.NODE_ENV !== "development") {
|
||||
// not in prouction or development mode
|
||||
console.warn("Unexpected NODE_ENV value");
|
||||
}
|
||||
|
||||
// in development mode, return "//hostname:8000/api"
|
||||
return `//${window.location.hostname}:8000/api`;
|
||||
}
|
||||
|
||||
public name_door(day: number): string {
|
||||
return `Türchen ${day}`;
|
||||
}
|
||||
|
||||
public api_url(): string;
|
||||
public api_url(endpoint: string): string;
|
||||
public api_url(endpoint?: string): string {
|
||||
if (endpoint === undefined) {
|
||||
return this.api_baseurl;
|
||||
}
|
||||
|
||||
while (endpoint.startsWith("/")) {
|
||||
endpoint = endpoint.substring(1);
|
||||
}
|
||||
|
||||
return `${this.api_baseurl}/${endpoint}`;
|
||||
}
|
||||
|
||||
private _api_get<T>(endpoint: string): Promise<T>;
|
||||
private _api_get<T>(endpoint: string, responseType: ResponseType): Promise<T>;
|
||||
private _api_get<T>(
|
||||
endpoint: string,
|
||||
responseType: ResponseType = "json",
|
||||
): Promise<T> {
|
||||
const req_config = {
|
||||
auth: advent22Store().axios_creds,
|
||||
responseType: responseType,
|
||||
};
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
this.axios
|
||||
.get<T>(this.api_url(endpoint), req_config)
|
||||
.then((response) => resolve(response.data))
|
||||
.catch((reason) => {
|
||||
console.error(`Failed to query ${endpoint}: ${reason}`);
|
||||
reject([reason, endpoint]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public api_get<T>(endpoint: string): Promise<T> {
|
||||
return this._api_get<T>(endpoint);
|
||||
}
|
||||
|
||||
public api_get_blob(endpoint: string): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
this._api_get<Blob>(endpoint, "blob")
|
||||
.then((data: Blob) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(data);
|
||||
reader.onloadend = () => {
|
||||
if (typeof reader.result === "string") {
|
||||
resolve(reader.result);
|
||||
} else {
|
||||
reject(["failed data url", endpoint]);
|
||||
}
|
||||
};
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
public api_put(endpoint: string, data: unknown): Promise<void> {
|
||||
const req_config = {
|
||||
auth: advent22Store().axios_creds,
|
||||
};
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.axios
|
||||
.put(this.api_url(endpoint), data, req_config)
|
||||
.then(() => resolve())
|
||||
.catch((reason) => {
|
||||
console.error(`Failed to query ${endpoint}: ${reason}`);
|
||||
reject([reason, endpoint]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const Advent22Plugin: Plugin = {
|
||||
install(app: App) {
|
||||
app.config.globalProperties.$advent22 = new Advent22();
|
||||
},
|
||||
};
|
|
@ -4,11 +4,11 @@ import { App, Plugin } from "vue";
|
|||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
/* import specific icons */
|
||||
import { fab } from "@fortawesome/free-brands-svg-icons";
|
||||
// import { fab } from "@fortawesome/free-brands-svg-icons";
|
||||
import { fas } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
/* add icons to the library */
|
||||
library.add(fas, fab);
|
||||
library.add(fas);
|
||||
|
||||
/* import font awesome icon component */
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { expect } from "chai";
|
||||
|
||||
import { Rectangle } from "@/lib/rectangle";
|
||||
import { Vector2D } from "@/lib/vector2d";
|
||||
import { Rectangle } from "@/lib/rects/rectangle";
|
||||
import { Vector2D } from "@/lib/rects/vector2d";
|
||||
|
||||
describe("Rectangle Tests", () => {
|
||||
const v1 = new Vector2D(1, 2);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { expect } from "chai";
|
||||
|
||||
import { Vector2D } from "@/lib/vector2d";
|
||||
import { Vector2D } from "@/lib/rects/vector2d";
|
||||
|
||||
describe("Vector2D Tests", () => {
|
||||
const v = new Vector2D(1, 2);
|
||||
|
|
Loading…
Reference in a new issue