Skip to content

Commit

Permalink
Working frontend with api association
Browse files Browse the repository at this point in the history
  • Loading branch information
dyakovri committed Apr 8, 2024
1 parent b3eb2bd commit 1070d43
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 20 deletions.
2 changes: 1 addition & 1 deletion frontend/.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VITE_API_URL=/
VITE_AUTH_API_BASE_URL=https://api.profcomff.com
32 changes: 29 additions & 3 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import { useProfileStore } from './store';
import { onMounted } from 'vue';
const profileStore = useProfileStore();
onMounted(() => {
profileStore.fromUrl();
});
</script>

<template>
<div class="container">
<main class="container">
<RouterView />
</div>
</main>
<footer>
Made by <img src="./assets/logo.svg" class="logo" /> in association with
<img src="https://dyakov.space/files/Icon.svg" class="logo" />
</footer>
</template>

<style scoped>
.container {
width: 100%;
height: 100%;
}
footer {
position: sticky;
bottom: 0;
width: 100%;
color: gray;
text-align: center;
& .logo {
height: 30px;
vertical-align: middle;
}
}
</style>
38 changes: 31 additions & 7 deletions frontend/src/api/BaseApi.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import axios, { AxiosResponse } from 'axios';
import queryString from 'query-string';
import { useProfileStore } from '../store';

export interface DefaultResponse {
status: string;
message: string;
}

const profileStore = useProfileStore();
type Path = `/${string}` | '';

export class BaseApi {
url: string;

constructor(path: Path) {
this.url = import.meta.env.VITE_API_URL + path;
constructor(path: Path, base: string = document.location.origin) {
this.url = base + path;
}

private ensureToken(): string | null {
if (!profileStore.token) profileStore.fromUrl();
if (profileStore.token) return profileStore.token;
return null;
}

protected async get<Response, Params = never>(
path: Path,
params?: Partial<Params>,
headers: Record<string, string> = {},
): Promise<AxiosResponse<Response>> {
if (!headers.Authorization) {
const token = this.ensureToken();
if (token) headers.Authorization = token;
}
return axios.get<Response>(`${this.url}${path}`, {
params,
headers,
Expand All @@ -35,6 +42,10 @@ export class BaseApi {
params?: Params,
headers: Record<string, string> = {},
): Promise<AxiosResponse<Response>> {
if (!headers.Authorization) {
const token = this.ensureToken();
if (token) headers.Authorization = token;
}
return axios.post<Response, AxiosResponse<Response>, Body>(`${this.url}${path}`, body, { headers, params });
}

Expand All @@ -43,6 +54,10 @@ export class BaseApi {
params?: Params,
headers: Record<string, string> = {},
): Promise<AxiosResponse<Response>> {
if (!headers.Authorization) {
const token = this.ensureToken();
if (token) headers.Authorization = token;
}
return axios.delete<Response>(`${this.url}${path}`, { params, headers });
}

Expand All @@ -51,14 +66,23 @@ export class BaseApi {
body?: Body,
headers: Record<string, string> = {},
): Promise<AxiosResponse<Response>> {
if (!headers.Authorization) {
const token = this.ensureToken();
if (token) headers.Authorization = token;
}
return axios.patch<Response, AxiosResponse<Response>, Body>(`${this.url}${path}`, body, { headers });
}

protected async put<Response = never, Body = never, Params = never>(
path: Path,
body?: Body,
params?: Params,
headers: Record<string, string> = {},
): Promise<AxiosResponse<Response>> {
if (!headers.Authorization) {
const token = this.ensureToken();
if (token) headers.Authorization = token;
}
return axios.put(`${this.url}${path}`, body, { params });
}
}
3 changes: 3 additions & 0 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { authMeApi, authUserApi, authGroupApi } from './user/AuthApi';
export { userdataUserApi } from './user/UserdataApi.ts';
export { touchMeApi } from './touch/touch';
17 changes: 17 additions & 0 deletions frontend/src/api/touch/touch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BaseApi } from '../BaseApi';

export interface TouchResponse {
id: string;
count?: string;
}

class TouchMeApi extends BaseApi {
constructor() {
super('', import.meta.env.VITE_API_BASE_URL ?? document.location.origin);
}
public async touch() {
return this.post<TouchResponse>('/touch');
}
}

export const touchMeApi = new TouchMeApi();
125 changes: 125 additions & 0 deletions frontend/src/api/user/AuthApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { BaseApi } from '../BaseApi';

export interface User {
id: string;
email?: string;
}

export interface DefaultResponse {
status: string;
message: string;
ru?: string;
}

export interface Scope {
id: number;
name: string;
}

export interface Group {
id: number;
name: string;
parent_id: number | null;
}

interface CreateGroupBody {
name: string;
parent_id: number;
scopes: number[];
}

export enum UserInfo {
Groups = 'groups',
IndirectGroups = 'indirect_groups',
SessionScopes = 'session_scopes',
UserScopes = 'user_scopes',
AuthMethods = 'auth_methods',
}

export enum GroupInfo {
Children = 'child',
Scopes = 'scopes',
IndirectScopes = 'indirect_scopes',
Users = 'users',
}

type UserResponse<Info extends UserInfo> = User & {
[UserInfo.Groups]: UserInfo.Groups extends Info ? number[] : never;
[UserInfo.IndirectGroups]: UserInfo.IndirectGroups extends Info ? number[] : never;
[UserInfo.SessionScopes]: UserInfo.SessionScopes extends Info ? string[] : never;
[UserInfo.UserScopes]: UserInfo.UserScopes extends Info ? number[] : never;
[UserInfo.AuthMethods]: UserInfo.AuthMethods extends Info ? string[] : never;
};

type GetGroupResponse<Info extends GroupInfo> = Group & {
[GroupInfo.Scopes]: GroupInfo.Scopes extends Info ? Scope[] : never;
[GroupInfo.IndirectScopes]: GroupInfo.IndirectScopes extends Info ? Scope[] : never;
[GroupInfo.Children]: GroupInfo.Children extends Info ? Group[] : never;
[GroupInfo.Users]: never;
};

export class AuthBaseApi extends BaseApi {
constructor(path = '') {
super(`/auth${path}`, import.meta.env.VITE_AUTH_API_BASE_URL);
}
}

class AuthMeApi extends AuthBaseApi {
constructor() {
super('');
}
public async logout() {
return this.post<DefaultResponse>('/logout');
}
public async getMe<Info extends UserInfo = never>(info?: Info[]) {
return this.get<UserResponse<Info>, { info?: Info[] }>('/me', { info });
}
}

class AuthUserApi extends AuthBaseApi {
constructor() {
super('/user');
}

public async getUser<Info extends UserInfo = never>(id: number, info: Info[]) {
return this.get<UserResponse<Info>, { info: Info[] }>(`/${id}`, { info });
}

public async deleteUser(id: number) {
return this.delete<string>(`/${id}`);
}

public async getUsers<Info extends UserInfo = never>(info: Info[]) {
return this.get<{ items: UserResponse<Info>[] }, { info: Info[] }>('', { info });
}
}

class AuthGroupApi extends AuthBaseApi {
constructor() {
super('/group');
}

public async getGroup<Info extends GroupInfo = never>(id: number, info?: Info[]) {
return this.get<GetGroupResponse<Info>, { info?: Info[] }>(`/${id}`, { info });
}

public async deleteGroup(id: number) {
return this.delete<string>(`/${id}`, undefined);
}

public async patchGroup(id: number, body: Partial<CreateGroupBody>) {
return this.patch<Group, Partial<CreateGroupBody>>(`/${id}`, body);
}

public async getGroups<Info extends GroupInfo>(info: Info[]) {
return this.get<{ items: GetGroupResponse<Info>[] }, { info: Info[] }>('', { info });
}

public async createGroup(body: CreateGroupBody) {
return this.post<Group, CreateGroupBody>('', body);
}
}

export const authMeApi = new AuthMeApi();
export const authUserApi = new AuthUserApi();
export const authGroupApi = new AuthGroupApi();
64 changes: 64 additions & 0 deletions frontend/src/api/user/UserdataApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { BaseApi } from '../BaseApi';

export enum UserdataParamResponseType {
All = 'all',
Last = 'last',
MostTrusted = 'most_trusted',
}

export interface UserdataRawItem {
category: string;
param: string;
value: string;
}

export interface UserdataExtendedValue {
name: string;
is_required?: boolean;
changeable?: boolean;
type?: UserdataParamResponseType;
}

export interface UserdataItem {
category: string;
param: string;
value: UserdataExtendedValue;
}

export interface UserdataRaw {
items: UserdataRawItem[];
}

export class UserdataBaseApi extends BaseApi {
constructor(path = '') {
super(`/userdata${path}`, import.meta.env.VITE_AUTH_API_BASE_URL);
}
}

export interface UserdataUpdateUserItem {
category: string;
param: string;
value: string | null;
}

export interface UserdataUpdateUser {
items: UserdataUpdateUserItem[];
source: string;
}

type Json = Record<string, Record<string, string>>;

class UserdataUserApi extends UserdataBaseApi {
constructor() {
super('/user');
}

public async getById(id: number) {
return this.get<UserdataRaw>(`/${id}`);
}

public async patchById(id: number, items: UserdataUpdateUserItem[], source: string = 'user') {
return this.post<Json, UserdataUpdateUser>(`/${id}`, { items, source });
}
}
export const userdataUserApi = new UserdataUserApi();
16 changes: 15 additions & 1 deletion frontend/src/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1070d43

Please sign in to comment.