Added totp/mail otp, split up dto and api into multiple files
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
<script setup async lang="ts">
 | 
			
		||||
import { provide, ref } from 'vue';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import { refresh_token, TokenInjectType, isErrorResponse } from '@/api';
 | 
			
		||||
import { Auth, TokenInjectType, isErrorResponse } from '@/api';
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 | 
			
		||||
@@ -21,7 +21,7 @@ function logout() {
 | 
			
		||||
jwt.value = localStorage.getItem('token');
 | 
			
		||||
if (jwt.value == null) await router.push({ name: 'login' });
 | 
			
		||||
else {
 | 
			
		||||
	const new_token = await refresh_token(jwt.value ?? '');
 | 
			
		||||
	const new_token = await Auth.refresh_token(jwt.value ?? '');
 | 
			
		||||
	if (isErrorResponse(new_token)) logout();
 | 
			
		||||
	else setToken(new_token.jwt);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,221 +0,0 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import {
 | 
			
		||||
	AuthLoginRequest,
 | 
			
		||||
	AuthSignUpRequest,
 | 
			
		||||
	BaseRequest,
 | 
			
		||||
	BaseResponse,
 | 
			
		||||
	CreateFileRequest,
 | 
			
		||||
	CreateFileResponse,
 | 
			
		||||
	CreateFolderRequest,
 | 
			
		||||
	CreateFolderResponse,
 | 
			
		||||
	DeleteRequest,
 | 
			
		||||
	DeleteResponse,
 | 
			
		||||
	ErrorResponse,
 | 
			
		||||
	GetNodeResponse,
 | 
			
		||||
	GetPathResponse,
 | 
			
		||||
	GetRootResponse,
 | 
			
		||||
	LoginResponse,
 | 
			
		||||
	RefreshResponse,
 | 
			
		||||
	SignupResponse,
 | 
			
		||||
	UploadFileResponse
 | 
			
		||||
} from '../../dto';
 | 
			
		||||
import jwtDecode, { JwtPayload } from 'jwt-decode';
 | 
			
		||||
import { Ref, UnwrapRef } from 'vue';
 | 
			
		||||
 | 
			
		||||
export * from '../../dto';
 | 
			
		||||
 | 
			
		||||
const post = <T extends BaseRequest>(url: string, data: T) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.post(url, data, {
 | 
			
		||||
			headers: { 'Content-type': 'application/json' }
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
const post_token = <T extends BaseRequest>(
 | 
			
		||||
	url: string,
 | 
			
		||||
	data: T,
 | 
			
		||||
	token: string
 | 
			
		||||
) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.post(url, data, {
 | 
			
		||||
			headers: {
 | 
			
		||||
				Authorization: 'Bearer ' + token,
 | 
			
		||||
				'Content-type': 'application/json'
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
const post_token_form = (
 | 
			
		||||
	url: string,
 | 
			
		||||
	data: FormData,
 | 
			
		||||
	token: string,
 | 
			
		||||
	onProgress: (progressEvent: ProgressEvent) => void
 | 
			
		||||
) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.post(url, data, {
 | 
			
		||||
			headers: {
 | 
			
		||||
				Authorization: 'Bearer ' + token,
 | 
			
		||||
				'Content-type': 'multipart/form-data'
 | 
			
		||||
			},
 | 
			
		||||
			onUploadProgress: onProgress
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
const get = (url: string) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.get(url)
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
const get_token = (url: string, token: string) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.get(url, {
 | 
			
		||||
			headers: { Authorization: 'Bearer ' + token }
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Api Requests
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
export const auth_login = (
 | 
			
		||||
	username: string,
 | 
			
		||||
	password: string
 | 
			
		||||
): Promise<LoginResponse | ErrorResponse> =>
 | 
			
		||||
	post<AuthLoginRequest>('/api/auth/login', {
 | 
			
		||||
		username: username,
 | 
			
		||||
		password: password
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
export const auth_signup = (
 | 
			
		||||
	username: string,
 | 
			
		||||
	password: string
 | 
			
		||||
): Promise<SignupResponse | ErrorResponse> =>
 | 
			
		||||
	post<AuthSignUpRequest>('/api/auth/signup', {
 | 
			
		||||
		username: username,
 | 
			
		||||
		password: password
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
export const get_root = (
 | 
			
		||||
	token: string
 | 
			
		||||
): Promise<GetRootResponse | ErrorResponse> => get_token('/api/fs/root', token);
 | 
			
		||||
 | 
			
		||||
export const get_node = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	node: number
 | 
			
		||||
): Promise<GetNodeResponse | ErrorResponse> =>
 | 
			
		||||
	get_token(`/api/fs/node/${node}`, token);
 | 
			
		||||
 | 
			
		||||
export const get_path = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	node: number
 | 
			
		||||
): Promise<GetPathResponse | ErrorResponse> =>
 | 
			
		||||
	get_token(`/api/fs/path/${node}`, token);
 | 
			
		||||
 | 
			
		||||
export const create_folder = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	parent: number,
 | 
			
		||||
	name: string
 | 
			
		||||
): Promise<CreateFolderResponse | ErrorResponse> =>
 | 
			
		||||
	post_token<CreateFolderRequest>(
 | 
			
		||||
		'/api/fs/createFolder',
 | 
			
		||||
		{
 | 
			
		||||
			parent: parent,
 | 
			
		||||
			name: name
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
export const create_file = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	parent: number,
 | 
			
		||||
	name: string
 | 
			
		||||
): Promise<CreateFileResponse | ErrorResponse> =>
 | 
			
		||||
	post_token<CreateFileRequest>(
 | 
			
		||||
		'/api/fs/createFile',
 | 
			
		||||
		{
 | 
			
		||||
			parent: parent,
 | 
			
		||||
			name: name
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
export const delete_node = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	node: number
 | 
			
		||||
): Promise<DeleteResponse | ErrorResponse> =>
 | 
			
		||||
	post_token<DeleteRequest>(
 | 
			
		||||
		'/api/fs/delete',
 | 
			
		||||
		{
 | 
			
		||||
			node: node
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
export const upload_file = async (
 | 
			
		||||
	token: string,
 | 
			
		||||
	parent: number,
 | 
			
		||||
	file: File,
 | 
			
		||||
	onProgress: (progressEvent: ProgressEvent) => void
 | 
			
		||||
): Promise<UploadFileResponse | ErrorResponse> => {
 | 
			
		||||
	const node = await create_file(token, parent, file.name);
 | 
			
		||||
	if (isErrorResponse(node)) return node;
 | 
			
		||||
 | 
			
		||||
	const form = new FormData();
 | 
			
		||||
	form.set('file', file);
 | 
			
		||||
	return post_token_form(
 | 
			
		||||
		`/api/fs/upload/${node.id}`,
 | 
			
		||||
		form,
 | 
			
		||||
		token,
 | 
			
		||||
		onProgress
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function download_file(token: string, id: number) {
 | 
			
		||||
	const form = document.createElement('form');
 | 
			
		||||
	form.method = 'post';
 | 
			
		||||
	form.target = '_blank';
 | 
			
		||||
	form.action = '/api/fs/download';
 | 
			
		||||
	form.innerHTML = `<input type="hidden" name="jwtToken" value="${token}"><input type="hidden" name="id" value="${id}">`;
 | 
			
		||||
	document.body.appendChild(form);
 | 
			
		||||
	form.submit();
 | 
			
		||||
	document.body.removeChild(form);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const refresh_token = (
 | 
			
		||||
	token: string
 | 
			
		||||
): Promise<RefreshResponse | ErrorResponse> =>
 | 
			
		||||
	post_token('/api/auth/refresh', '', token);
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Utilities
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
export async function check_token(
 | 
			
		||||
	token: TokenInjectType
 | 
			
		||||
): Promise<string | void> {
 | 
			
		||||
	if (!token.jwt.value) return token.logout();
 | 
			
		||||
	const payload = jwtDecode<JwtPayload>(token.jwt.value);
 | 
			
		||||
	if (!payload) return token.logout();
 | 
			
		||||
	// Expires in more than 60 Minute
 | 
			
		||||
	if (payload.exp && payload.exp > Math.floor(Date.now() / 1000 + 60 * 60))
 | 
			
		||||
		return token.jwt.value;
 | 
			
		||||
	const new_token = await refresh_token(token.jwt.value);
 | 
			
		||||
	if (isErrorResponse(new_token)) return token.logout();
 | 
			
		||||
	token.setToken(new_token.jwt);
 | 
			
		||||
	return new_token.jwt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const isErrorResponse = (res: BaseResponse): res is ErrorResponse =>
 | 
			
		||||
	res.statusCode != 200;
 | 
			
		||||
 | 
			
		||||
export type TokenInjectType = {
 | 
			
		||||
	jwt: Ref<UnwrapRef<string | null>>;
 | 
			
		||||
	setToken: (token: string) => void;
 | 
			
		||||
	logout: () => void;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										30
									
								
								frontend/src/api/auth.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								frontend/src/api/auth.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
import { Responses, Requests, post, post_token } from './base';
 | 
			
		||||
 | 
			
		||||
export const auth_login = (
 | 
			
		||||
	username: string,
 | 
			
		||||
	password: string,
 | 
			
		||||
	otp?: string
 | 
			
		||||
): Promise<
 | 
			
		||||
	| Responses.Auth.LoginResponse
 | 
			
		||||
	| Responses.Auth.TfaRequiredResponse
 | 
			
		||||
	| Responses.ErrorResponse
 | 
			
		||||
> =>
 | 
			
		||||
	post<Requests.Auth.AuthLoginRequest>('/api/auth/login', {
 | 
			
		||||
		username: username,
 | 
			
		||||
		password: password,
 | 
			
		||||
		otp: otp
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
export const auth_signup = (
 | 
			
		||||
	username: string,
 | 
			
		||||
	password: string
 | 
			
		||||
): Promise<Responses.Auth.SignupResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post<Requests.Auth.AuthSignUpRequest>('/api/auth/signup', {
 | 
			
		||||
		username: username,
 | 
			
		||||
		password: password
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
export const refresh_token = (
 | 
			
		||||
	token: string
 | 
			
		||||
): Promise<Responses.Auth.RefreshResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token('/api/auth/refresh', '', token);
 | 
			
		||||
							
								
								
									
										63
									
								
								frontend/src/api/base.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								frontend/src/api/base.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import { Requests, Responses } from '../../../dto';
 | 
			
		||||
 | 
			
		||||
export * from '../../../dto';
 | 
			
		||||
 | 
			
		||||
export const post = <T extends Requests.BaseRequest>(url: string, data: T) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.post(url, data, {
 | 
			
		||||
			headers: { 'Content-type': 'application/json' }
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
export const post_token = <T extends Requests.BaseRequest>(
 | 
			
		||||
	url: string,
 | 
			
		||||
	data: T,
 | 
			
		||||
	token: string
 | 
			
		||||
) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.post(url, data, {
 | 
			
		||||
			headers: {
 | 
			
		||||
				Authorization: 'Bearer ' + token,
 | 
			
		||||
				'Content-type': 'application/json'
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
export const post_token_form = (
 | 
			
		||||
	url: string,
 | 
			
		||||
	data: FormData,
 | 
			
		||||
	token: string,
 | 
			
		||||
	onProgress: (progressEvent: ProgressEvent) => void
 | 
			
		||||
) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.post(url, data, {
 | 
			
		||||
			headers: {
 | 
			
		||||
				Authorization: 'Bearer ' + token,
 | 
			
		||||
				'Content-type': 'multipart/form-data'
 | 
			
		||||
			},
 | 
			
		||||
			onUploadProgress: onProgress
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
export const get = (url: string) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.get(url)
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
export const get_token = (url: string, token: string) =>
 | 
			
		||||
	axios
 | 
			
		||||
		.get(url, {
 | 
			
		||||
			headers: { Authorization: 'Bearer ' + token }
 | 
			
		||||
		})
 | 
			
		||||
		.then((res) => res.data)
 | 
			
		||||
		.catch((err) => err.response.data);
 | 
			
		||||
 | 
			
		||||
export const isErrorResponse = (
 | 
			
		||||
	res: Responses.BaseResponse
 | 
			
		||||
): res is Responses.ErrorResponse => res.statusCode != 200;
 | 
			
		||||
							
								
								
									
										95
									
								
								frontend/src/api/fs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								frontend/src/api/fs.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
import {
 | 
			
		||||
	Responses,
 | 
			
		||||
	Requests,
 | 
			
		||||
	get_token,
 | 
			
		||||
	post_token,
 | 
			
		||||
	post_token_form,
 | 
			
		||||
	isErrorResponse
 | 
			
		||||
} from './base';
 | 
			
		||||
 | 
			
		||||
export const get_root = (
 | 
			
		||||
	token: string
 | 
			
		||||
): Promise<Responses.FS.GetRootResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	get_token('/api/fs/root', token);
 | 
			
		||||
 | 
			
		||||
export const get_node = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	node: number
 | 
			
		||||
): Promise<Responses.FS.GetNodeResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	get_token(`/api/fs/node/${node}`, token);
 | 
			
		||||
 | 
			
		||||
export const get_path = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	node: number
 | 
			
		||||
): Promise<Responses.FS.GetPathResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	get_token(`/api/fs/path/${node}`, token);
 | 
			
		||||
 | 
			
		||||
export const create_folder = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	parent: number,
 | 
			
		||||
	name: string
 | 
			
		||||
): Promise<Responses.FS.CreateFolderResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token<Requests.FS.CreateFolderRequest>(
 | 
			
		||||
		'/api/fs/createFolder',
 | 
			
		||||
		{
 | 
			
		||||
			parent: parent,
 | 
			
		||||
			name: name
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
export const create_file = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	parent: number,
 | 
			
		||||
	name: string
 | 
			
		||||
): Promise<Responses.FS.CreateFileResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token<Requests.FS.CreateFileRequest>(
 | 
			
		||||
		'/api/fs/createFile',
 | 
			
		||||
		{
 | 
			
		||||
			parent: parent,
 | 
			
		||||
			name: name
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
export const delete_node = (
 | 
			
		||||
	token: string,
 | 
			
		||||
	node: number
 | 
			
		||||
): Promise<Responses.FS.DeleteResponse | Responses.ErrorResponse> =>
 | 
			
		||||
	post_token<Requests.FS.DeleteRequest>(
 | 
			
		||||
		'/api/fs/delete',
 | 
			
		||||
		{
 | 
			
		||||
			node: node
 | 
			
		||||
		},
 | 
			
		||||
		token
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
export const upload_file = async (
 | 
			
		||||
	token: string,
 | 
			
		||||
	parent: number,
 | 
			
		||||
	file: File,
 | 
			
		||||
	onProgress: (progressEvent: ProgressEvent) => void
 | 
			
		||||
): Promise<Responses.FS.UploadFileResponse | Responses.ErrorResponse> => {
 | 
			
		||||
	const node = await create_file(token, parent, file.name);
 | 
			
		||||
	if (isErrorResponse(node)) return node;
 | 
			
		||||
 | 
			
		||||
	const form = new FormData();
 | 
			
		||||
	form.set('file', file);
 | 
			
		||||
	return post_token_form(
 | 
			
		||||
		`/api/fs/upload/${node.id}`,
 | 
			
		||||
		form,
 | 
			
		||||
		token,
 | 
			
		||||
		onProgress
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function download_file(token: string, id: number) {
 | 
			
		||||
	const form = document.createElement('form');
 | 
			
		||||
	form.method = 'post';
 | 
			
		||||
	form.target = '_blank';
 | 
			
		||||
	form.action = '/api/fs/download';
 | 
			
		||||
	form.innerHTML = `<input type="hidden" name="jwtToken" value="${token}"><input type="hidden" name="id" value="${id}">`;
 | 
			
		||||
	document.body.appendChild(form);
 | 
			
		||||
	form.submit();
 | 
			
		||||
	document.body.removeChild(form);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								frontend/src/api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								frontend/src/api/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
export { Requests, Responses, isErrorResponse } from './base';
 | 
			
		||||
export * as Auth from './auth';
 | 
			
		||||
export * as FS from './fs';
 | 
			
		||||
export * from './util';
 | 
			
		||||
							
								
								
									
										25
									
								
								frontend/src/api/util.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								frontend/src/api/util.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import jwtDecode, { JwtPayload } from 'jwt-decode';
 | 
			
		||||
import { Ref, UnwrapRef } from 'vue';
 | 
			
		||||
import { isErrorResponse } from './base';
 | 
			
		||||
import { refresh_token } from './auth';
 | 
			
		||||
 | 
			
		||||
export async function check_token(
 | 
			
		||||
	token: TokenInjectType
 | 
			
		||||
): Promise<string | void> {
 | 
			
		||||
	if (!token.jwt.value) return token.logout();
 | 
			
		||||
	const payload = jwtDecode<JwtPayload>(token.jwt.value);
 | 
			
		||||
	if (!payload) return token.logout();
 | 
			
		||||
	// Expires in more than 60 Minute
 | 
			
		||||
	if (payload.exp && payload.exp > Math.floor(Date.now() / 1000 + 60 * 60))
 | 
			
		||||
		return token.jwt.value;
 | 
			
		||||
	const new_token = await refresh_token(token.jwt.value);
 | 
			
		||||
	if (isErrorResponse(new_token)) return token.logout();
 | 
			
		||||
	token.setToken(new_token.jwt);
 | 
			
		||||
	return new_token.jwt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type TokenInjectType = {
 | 
			
		||||
	jwt: Ref<UnwrapRef<string | null>>;
 | 
			
		||||
	setToken: (token: string) => void;
 | 
			
		||||
	logout: () => void;
 | 
			
		||||
};
 | 
			
		||||
@@ -1,16 +1,10 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineEmits, defineProps, inject } from 'vue';
 | 
			
		||||
import {
 | 
			
		||||
	check_token,
 | 
			
		||||
	delete_node,
 | 
			
		||||
	download_file,
 | 
			
		||||
	GetNodeResponse,
 | 
			
		||||
	TokenInjectType
 | 
			
		||||
} from '@/api';
 | 
			
		||||
import { check_token, FS, Responses, TokenInjectType } from '@/api';
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	node: GetNodeResponse;
 | 
			
		||||
	node: Responses.FS.GetNodeResponse;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
@@ -20,14 +14,14 @@ const emit = defineEmits<{
 | 
			
		||||
async function del() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	await delete_node(token, props.node.id);
 | 
			
		||||
	await FS.delete_node(token, props.node.id);
 | 
			
		||||
	emit('reloadNode');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function download() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	download_file(token, props.node.id);
 | 
			
		||||
	FS.download_file(token, props.node.id);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,12 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineEmits, defineProps, inject, reactive, ref, watch } from 'vue';
 | 
			
		||||
import {
 | 
			
		||||
	GetNodeResponse,
 | 
			
		||||
	create_folder,
 | 
			
		||||
	get_node,
 | 
			
		||||
	check_token,
 | 
			
		||||
	TokenInjectType
 | 
			
		||||
} from '@/api';
 | 
			
		||||
import { FS, Responses, check_token, TokenInjectType } from '@/api';
 | 
			
		||||
import DirEntry from '@/components/FSView/DirEntry.vue';
 | 
			
		||||
import UploadFileDialog from '@/components/UploadDialog/UploadFileDialog.vue';
 | 
			
		||||
import { NModal } from 'naive-ui';
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	node: GetNodeResponse;
 | 
			
		||||
	node: Responses.FS.GetNodeResponse;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
@@ -28,9 +22,9 @@ const uploadDialogShow = ref(false);
 | 
			
		||||
 | 
			
		||||
const new_folder_name = ref('');
 | 
			
		||||
const files = ref<File[]>([]);
 | 
			
		||||
const nodes = ref<GetNodeResponse[]>([]);
 | 
			
		||||
const nodes = ref<Responses.FS.GetNodeResponse[]>([]);
 | 
			
		||||
const hasParent = ref(false);
 | 
			
		||||
const parentNode = reactive<GetNodeResponse>({
 | 
			
		||||
const parentNode = reactive<Responses.FS.GetNodeResponse>({
 | 
			
		||||
	id: 0,
 | 
			
		||||
	statusCode: 200,
 | 
			
		||||
	isFile: false,
 | 
			
		||||
@@ -49,7 +43,10 @@ watch(
 | 
			
		||||
		await Promise.all(
 | 
			
		||||
			to.children?.map(async (child) => {
 | 
			
		||||
				nodes.value.push(
 | 
			
		||||
					(await get_node(token, child)) as GetNodeResponse
 | 
			
		||||
					(await FS.get_node(
 | 
			
		||||
						token,
 | 
			
		||||
						child
 | 
			
		||||
					)) as Responses.FS.GetNodeResponse
 | 
			
		||||
				);
 | 
			
		||||
			}) ?? []
 | 
			
		||||
		);
 | 
			
		||||
@@ -60,7 +57,7 @@ watch(
 | 
			
		||||
async function newFolder() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	await create_folder(token, props.node.id, new_folder_name.value);
 | 
			
		||||
	await FS.create_folder(token, props.node.id, new_folder_name.value);
 | 
			
		||||
	emit('reloadNode');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,9 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineProps, inject } from 'vue';
 | 
			
		||||
import {
 | 
			
		||||
	check_token,
 | 
			
		||||
	delete_node,
 | 
			
		||||
	download_file,
 | 
			
		||||
	GetNodeResponse,
 | 
			
		||||
	TokenInjectType
 | 
			
		||||
} from '@/api';
 | 
			
		||||
import { check_token, FS, Responses, TokenInjectType } from '@/api';
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	node: GetNodeResponse;
 | 
			
		||||
	node: Responses.FS.GetNodeResponse;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
@@ -17,13 +11,13 @@ const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
async function del() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	await delete_node(token, props.node.id);
 | 
			
		||||
	await FS.delete_node(token, props.node.id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function download() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	download_file(token, props.node.id);
 | 
			
		||||
	FS.download_file(token, props.node.id);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { defineProps, defineExpose, ref } from 'vue';
 | 
			
		||||
import { isErrorResponse, upload_file } from '@/api';
 | 
			
		||||
import { isErrorResponse, FS } from '@/api';
 | 
			
		||||
import { NProgress } from 'naive-ui';
 | 
			
		||||
import filesize from 'filesize';
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ const err = ref('');
 | 
			
		||||
const status = ref('info');
 | 
			
		||||
 | 
			
		||||
async function startUpload(parent: number, token: string) {
 | 
			
		||||
	const resp = await upload_file(token, parent, props.file, (e) => {
 | 
			
		||||
	const resp = await FS.upload_file(token, parent, props.file, (e) => {
 | 
			
		||||
		progress.value = e.loaded;
 | 
			
		||||
		percentage.value = (e.loaded / e.total) * 100;
 | 
			
		||||
	});
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,8 @@ import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 | 
			
		||||
import { inject, onBeforeMount, ref } from 'vue';
 | 
			
		||||
import {
 | 
			
		||||
	check_token,
 | 
			
		||||
	get_node,
 | 
			
		||||
	get_path,
 | 
			
		||||
	get_root,
 | 
			
		||||
	GetNodeResponse,
 | 
			
		||||
	FS,
 | 
			
		||||
	Responses,
 | 
			
		||||
	isErrorResponse,
 | 
			
		||||
	TokenInjectType
 | 
			
		||||
} from '@/api';
 | 
			
		||||
@@ -18,14 +16,14 @@ const route = useRoute();
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
const path = ref('');
 | 
			
		||||
const node = ref<GetNodeResponse | null>(null);
 | 
			
		||||
const node = ref<Responses.FS.GetNodeResponse | null>(null);
 | 
			
		||||
 | 
			
		||||
async function fetch_node(node_id: number) {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	let [p, n] = [
 | 
			
		||||
		await get_path(token, node_id),
 | 
			
		||||
		await get_node(token, node_id)
 | 
			
		||||
		await FS.get_path(token, node_id),
 | 
			
		||||
		await FS.get_node(token, node_id)
 | 
			
		||||
	];
 | 
			
		||||
	if (isErrorResponse(p)) return gotoRoot();
 | 
			
		||||
	if (isErrorResponse(n)) return gotoRoot();
 | 
			
		||||
@@ -46,7 +44,7 @@ onBeforeMount(async () => {
 | 
			
		||||
async function gotoRoot() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	const rootRes = await get_root(token);
 | 
			
		||||
	const rootRes = await FS.get_root(token);
 | 
			
		||||
	if (isErrorResponse(rootRes)) return jwt.logout();
 | 
			
		||||
	const root = rootRes.rootId;
 | 
			
		||||
	await router.replace({
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { onBeforeRouteUpdate, useRouter } from 'vue-router';
 | 
			
		||||
import { inject, onBeforeMount } from 'vue';
 | 
			
		||||
import { check_token, get_root, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
import { FS, check_token, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
@@ -11,7 +11,7 @@ const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
async function start_redirect() {
 | 
			
		||||
	const token = await check_token(jwt);
 | 
			
		||||
	if (!token) return;
 | 
			
		||||
	const root = await get_root(token);
 | 
			
		||||
	const root = await FS.get_root(token);
 | 
			
		||||
	if (isErrorResponse(root)) return jwt.logout();
 | 
			
		||||
	await router.push({
 | 
			
		||||
		name: 'fs',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,32 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, inject } from 'vue';
 | 
			
		||||
import { auth_login, get_root, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
import { Auth, FS, isErrorResponse, TokenInjectType } from '@/api';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 | 
			
		||||
let username = ref('');
 | 
			
		||||
let password = ref('');
 | 
			
		||||
const username = ref('');
 | 
			
		||||
const password = ref('');
 | 
			
		||||
const otp = ref('');
 | 
			
		||||
 | 
			
		||||
const error = ref('');
 | 
			
		||||
 | 
			
		||||
const requestOtp = ref(false);
 | 
			
		||||
 | 
			
		||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
 | 
			
		||||
 | 
			
		||||
async function login() {
 | 
			
		||||
	error.value = '';
 | 
			
		||||
	if (username.value === '' || password.value === '') {
 | 
			
		||||
		error.value = 'Email and/or Password missing';
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	const res = await auth_login(username.value, password.value);
 | 
			
		||||
	const res = await (requestOtp.value
 | 
			
		||||
		? Auth.auth_login(username.value, password.value, otp.value)
 | 
			
		||||
		: Auth.auth_login(username.value, password.value));
 | 
			
		||||
	if (isErrorResponse(res)) error.value = 'Login failed: ' + res.message;
 | 
			
		||||
	else {
 | 
			
		||||
		const root = await get_root(res.jwt);
 | 
			
		||||
	else if ('jwt' in res) {
 | 
			
		||||
		const root = await FS.get_root(res.jwt);
 | 
			
		||||
		if (isErrorResponse(root)) {
 | 
			
		||||
			error.value = 'Get root failed: ' + root.message;
 | 
			
		||||
			return;
 | 
			
		||||
@@ -29,14 +36,23 @@ async function login() {
 | 
			
		||||
			name: 'fs',
 | 
			
		||||
			params: { node_id: root.rootId }
 | 
			
		||||
		});
 | 
			
		||||
	} else {
 | 
			
		||||
		error.value = '';
 | 
			
		||||
		requestOtp.value = true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
	<div v-if="error !== ''" v-text="error"></div>
 | 
			
		||||
	<input type="email" placeholder="Email" v-model="username" />
 | 
			
		||||
	<input type="password" placeholder="Password" v-model="password" />
 | 
			
		||||
	<template v-if="!requestOtp">
 | 
			
		||||
		<input type="email" placeholder="Email" v-model="username" />
 | 
			
		||||
		<input type="password" placeholder="Password" v-model="password" />
 | 
			
		||||
	</template>
 | 
			
		||||
	<template v-else>
 | 
			
		||||
		<div>Please input your 2 factor authentication code</div>
 | 
			
		||||
		<input type="text" placeholder="Code" v-model="otp" />
 | 
			
		||||
	</template>
 | 
			
		||||
	<button @click="login()">Login</button>
 | 
			
		||||
	<router-link to="signup">Signup instead?</router-link>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { auth_signup, isErrorResponse } from '@/api';
 | 
			
		||||
import { Auth, isErrorResponse } from '@/api';
 | 
			
		||||
 | 
			
		||||
let username = ref('');
 | 
			
		||||
let password = ref('');
 | 
			
		||||
@@ -16,7 +16,7 @@ async function signup() {
 | 
			
		||||
		error.value = "Passwords don't match";
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	const res = await auth_signup(username.value, password.value);
 | 
			
		||||
	const res = await Auth.auth_signup(username.value, password.value);
 | 
			
		||||
	error.value = isErrorResponse(res)
 | 
			
		||||
		? 'Signup failed: ' + res.message
 | 
			
		||||
		: 'Signup successful, please wait till an admin unlocks your account.';
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user