mirror of
https://github.com/hay-kot/homebox.git
synced 2024-12-18 13:06:32 +00:00
cleanup
This commit is contained in:
parent
682774c9ce
commit
630fe83de5
5 changed files with 184 additions and 146 deletions
|
@ -1,9 +1,10 @@
|
||||||
{
|
{
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid",
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"tabWidth": 4,
|
"tabWidth": 2,
|
||||||
"useTabs": true,
|
"useTabs": false,
|
||||||
"vueIndentScriptAndStyle": true,
|
"vueIndentScriptAndStyle": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "es5"
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 120
|
||||||
}
|
}
|
|
@ -1,18 +1,22 @@
|
||||||
import { BaseAPI, UrlBuilder } from './base';
|
import { BaseAPI, UrlBuilder } from './base';
|
||||||
|
|
||||||
export type Result<T> = {
|
export type Result<T> = {
|
||||||
item: T;
|
item: T;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
isSuperuser: boolean;
|
isSuperuser: boolean;
|
||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class UserApi extends BaseAPI {
|
export class UserApi extends BaseAPI {
|
||||||
public self() {
|
public self() {
|
||||||
return this.http.get<Result<User>>(UrlBuilder('/users/self'));
|
return this.http.get<Result<User>>(UrlBuilder('/users/self'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public logout() {
|
||||||
|
return this.http.post<object, void>(UrlBuilder('/users/logout'), {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,97 @@
|
||||||
export enum Method {
|
export enum Method {
|
||||||
GET = 'GET',
|
GET = 'GET',
|
||||||
POST = 'POST',
|
POST = 'POST',
|
||||||
PUT = 'PUT',
|
PUT = 'PUT',
|
||||||
DELETE = 'DELETE',
|
DELETE = 'DELETE',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TResponse<T> {
|
export interface TResponse<T> {
|
||||||
status: number;
|
status: number;
|
||||||
error: boolean;
|
error: boolean;
|
||||||
data: T;
|
data: T;
|
||||||
response: Response;
|
response: Response;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Requests {
|
export class Requests {
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
private token: () => string;
|
private token: () => string;
|
||||||
private headers: Record<string, string> = {};
|
private headers: Record<string, string> = {};
|
||||||
private logger?: (response: Response) => void;
|
private logger?: (response: Response) => void;
|
||||||
|
|
||||||
private url(rest: string): string {
|
private url(rest: string): string {
|
||||||
return this.baseUrl + rest;
|
return this.baseUrl + rest;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
token: string | (() => string) = '',
|
token: string | (() => string) = '',
|
||||||
headers: Record<string, string> = {},
|
headers: Record<string, string> = {},
|
||||||
logger?: (response: Response) => void
|
logger?: (response: Response) => void
|
||||||
) {
|
) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.token = typeof token === 'string' ? () => token : token;
|
this.token = typeof token === 'string' ? () => token : token;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get<T>(url: string): Promise<TResponse<T>> {
|
public get<T>(url: string): Promise<TResponse<T>> {
|
||||||
return this.do<T>(Method.GET, url);
|
return this.do<T>(Method.GET, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public post<T, U>(url: string, payload: T): Promise<TResponse<U>> {
|
public post<T, U>(url: string, payload: T): Promise<TResponse<U>> {
|
||||||
return this.do<U>(Method.POST, url, payload);
|
return this.do<U>(Method.POST, url, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public put<T, U>(url: string, payload: T): Promise<TResponse<U>> {
|
public put<T, U>(url: string, payload: T): Promise<TResponse<U>> {
|
||||||
return this.do<U>(Method.PUT, url, payload);
|
return this.do<U>(Method.PUT, url, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public delete<T>(url: string): Promise<TResponse<T>> {
|
public delete<T>(url: string): Promise<TResponse<T>> {
|
||||||
return this.do<T>(Method.DELETE, url);
|
return this.do<T>(Method.DELETE, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private methodSupportsBody(method: Method): boolean {
|
private methodSupportsBody(method: Method): boolean {
|
||||||
return method === Method.POST || method === Method.PUT;
|
return method === Method.POST || method === Method.PUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async do<T>(
|
private async do<T>(method: Method, url: string, payload: Object = {}): Promise<TResponse<T>> {
|
||||||
method: Method,
|
const args: RequestInit = {
|
||||||
url: string,
|
method,
|
||||||
payload: Object = {}
|
headers: {
|
||||||
): Promise<TResponse<T>> {
|
'Content-Type': 'application/json',
|
||||||
const args: RequestInit = {
|
...this.headers,
|
||||||
method,
|
},
|
||||||
headers: {
|
};
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...this.headers,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const token = this.token();
|
const token = this.token();
|
||||||
if (token !== '' && args.headers !== undefined) {
|
if (token !== '' && args.headers !== undefined) {
|
||||||
// @ts-expect-error -- headers is always defined at this point
|
// @ts-expect-error -- headers is always defined at this point
|
||||||
args.headers['Authorization'] = token;
|
args.headers['Authorization'] = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.methodSupportsBody(method)) {
|
if (this.methodSupportsBody(method)) {
|
||||||
args.body = JSON.stringify(payload);
|
args.body = JSON.stringify(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(this.url(url), args);
|
const response = await fetch(this.url(url), args);
|
||||||
|
|
||||||
if (this.logger) {
|
if (this.logger) {
|
||||||
this.logger(response);
|
this.logger(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data: T = await (async () => {
|
||||||
|
try {
|
||||||
|
return await response.json();
|
||||||
|
} catch (e) {
|
||||||
|
return {} as T;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
error: !response.ok,
|
error: !response.ok,
|
||||||
data,
|
data,
|
||||||
response,
|
response,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,73 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useUserApi } from '@/composables/use-api';
|
import { useUserApi } from '@/composables/use-api';
|
||||||
useHead({
|
import { useAuthStore } from '@/store/auth';
|
||||||
title: 'Homebox | Home',
|
useHead({
|
||||||
});
|
title: 'Homebox | Home',
|
||||||
|
});
|
||||||
|
|
||||||
const links = [
|
const api = useUserApi();
|
||||||
{
|
|
||||||
name: 'Home',
|
|
||||||
href: '/home',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Logout',
|
|
||||||
href: '/logout',
|
|
||||||
last: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const api = useUserApi();
|
|
||||||
|
|
||||||
const user = ref({});
|
const user = ref({});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const { data } = await api.self();
|
const { data } = await api.self();
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
user.value = data.item;
|
user.value = data.item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
const { error } = await authStore.logout(api);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{
|
||||||
|
name: 'Home',
|
||||||
|
href: '/home',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Logout',
|
||||||
|
action: logout,
|
||||||
|
last: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="max-w-7xl mx-auto">
|
<section class="max-w-7xl mx-auto">
|
||||||
<header class="sm:px-6 py-2 lg:p-14 sm:py-6">
|
<header class="sm:px-6 py-2 lg:p-14 sm:py-6">
|
||||||
<h2
|
<h2 class="mt-1 text-4xl font-bold tracking-tight text-gray-200 sm:text-5xl lg:text-6xl">Homebox</h2>
|
||||||
class="mt-1 text-4xl font-bold tracking-tight text-gray-200 sm:text-5xl lg:text-6xl"
|
<div class="ml-1 text-lg text-gray-400 space-x-2">
|
||||||
>
|
<template v-for="link in links">
|
||||||
Homebox
|
<router-link
|
||||||
</h2>
|
v-if="!link.action"
|
||||||
<div class="ml-1 text-lg text-gray-400 space-x-2 italic">
|
class="hover:text-base-content transition-color duration-200 italic"
|
||||||
<template v-for="link in links">
|
:to="link.href"
|
||||||
<router-link
|
>
|
||||||
class="hover:text-base-content transition-color duration-200"
|
{{ link.name }}
|
||||||
:to="link.href"
|
</router-link>
|
||||||
>
|
<button v-else @click="link.action" class="hover:text-base-content transition-color duration-200 italic">
|
||||||
{{ link.name }}
|
{{ link.name }}
|
||||||
</router-link>
|
</button>
|
||||||
<span v-if="!link.last"> / </span>
|
<span v-if="!link.last"> / </span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</section>
|
</section>
|
||||||
<section class="max-w-7xl mx-auto sm:px-6 lg:px-14">
|
<section class="max-w-7xl mx-auto sm:px-6 lg:px-14">
|
||||||
{{ user }}
|
{{ user }}
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<route lang="yaml">
|
<route lang="yaml">
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
|
import { UserApi } from '@/api/user';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', {
|
export const useAuthStore = defineStore('auth', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
token: useLocalStorage('pinia/auth/token', ''),
|
token: useLocalStorage('pinia/auth/token', ''),
|
||||||
expires: useLocalStorage('pinia/auth/expires', ''),
|
expires: useLocalStorage('pinia/auth/expires', ''),
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
isTokenExpired: state => {
|
isTokenExpired: state => {
|
||||||
if (!state.expires) {
|
if (!state.expires) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof state.expires === 'string') {
|
if (typeof state.expires === 'string') {
|
||||||
return new Date(state.expires) < new Date();
|
return new Date(state.expires) < new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.expires < new Date();
|
return state.expires < new Date();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
actions: {
|
||||||
|
async logout(api: UserApi) {
|
||||||
|
const result = await api.logout();
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.token = '';
|
||||||
|
this.expires = '';
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue