Compare commits
No commits in common. "e98bd1f647f85a1b055646b3e662b9207f40681f" and "fcf8f7e3ce5d056aafd120f937778aa8e5045465" have entirely different histories.
e98bd1f647
...
fcf8f7e3ce
|
@ -1400,6 +1400,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/web-bluetooth": {
|
||||||
|
"version": "0.0.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||||
|
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/yauzl": {
|
"node_modules/@types/yauzl": {
|
||||||
"version": "2.10.3",
|
"version": "2.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||||
|
@ -2029,6 +2035,56 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vueuse/core": {
|
||||||
|
"version": "10.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz",
|
||||||
|
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/web-bluetooth": "^0.0.20",
|
||||||
|
"@vueuse/metadata": "10.11.1",
|
||||||
|
"@vueuse/shared": "10.11.1",
|
||||||
|
"vue-demi": ">=0.14.8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||||
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/metadata": {
|
||||||
|
"version": "10.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
||||||
|
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vueuse/shared": {
|
"node_modules/@vueuse/shared": {
|
||||||
"version": "10.11.1",
|
"version": "10.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
|
||||||
|
@ -6343,62 +6399,6 @@
|
||||||
"vue": ">= 3.2.0"
|
"vue": ">= 3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/radix-vue/node_modules/@types/web-bluetooth": {
|
|
||||||
"version": "0.0.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
|
||||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@vueuse/core": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/web-bluetooth": "^0.0.20",
|
|
||||||
"@vueuse/metadata": "10.11.1",
|
|
||||||
"@vueuse/shared": "10.11.1",
|
|
||||||
"vue-demi": ">=0.14.8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@vueuse/core/node_modules/vue-demi": {
|
|
||||||
"version": "0.14.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
|
||||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
||||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@vue/composition-api": "^1.0.0-rc.1",
|
|
||||||
"vue": "^3.0.0-0 || ^2.6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@vue/composition-api": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@vueuse/metadata": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/nanoid": {
|
"node_modules/radix-vue/node_modules/nanoid": {
|
||||||
"version": "5.0.9",
|
"version": "5.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz",
|
||||||
|
|
|
@ -421,69 +421,3 @@ tr:last-child > td:first-child {
|
||||||
tr:last-child > td:last-child {
|
tr:last-child > td:last-child {
|
||||||
border-bottom-right-radius: 4px;
|
border-bottom-right-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-container nav.sidebar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-container .view {
|
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
nav.sidebar h3 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--overlay-0);
|
|
||||||
padding: 0 8px;
|
|
||||||
font-size: 8pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar > .categories {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 192px;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar a.tab {
|
|
||||||
font-size: 11pt;
|
|
||||||
color: var(--overlay-0);
|
|
||||||
padding: 6px 10px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar a.tab:hover {
|
|
||||||
background-color: var(--crust);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar a.tab.router-link-exact-active {
|
|
||||||
background-color: var(--crust);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar button {
|
|
||||||
font-size: 11pt;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 6px 10px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: var(--overlay-0);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar button:hover {
|
|
||||||
background-color: var(--crust);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sidebar button.destructive-on-hover:hover {
|
|
||||||
background-color: var(--destructive);
|
|
||||||
color: var(--base);
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,12 +22,10 @@ export type { EventWithPlayerSchema } from './models/EventWithPlayerSchema';
|
||||||
export type { EventWithPlayerSchemaList } from './models/EventWithPlayerSchemaList';
|
export type { EventWithPlayerSchemaList } from './models/EventWithPlayerSchemaList';
|
||||||
export type { GetEventPlayersResponse } from './models/GetEventPlayersResponse';
|
export type { GetEventPlayersResponse } from './models/GetEventPlayersResponse';
|
||||||
export type { GetMatchQuery } from './models/GetMatchQuery';
|
export type { GetMatchQuery } from './models/GetMatchQuery';
|
||||||
export type { GetUserResponse } from './models/GetUserResponse';
|
|
||||||
export type { MatchSchema } from './models/MatchSchema';
|
export type { MatchSchema } from './models/MatchSchema';
|
||||||
export type { PlayerEventRolesSchema } from './models/PlayerEventRolesSchema';
|
export type { PlayerEventRolesSchema } from './models/PlayerEventRolesSchema';
|
||||||
export type { PlayerRoleSchema } from './models/PlayerRoleSchema';
|
export type { PlayerRoleSchema } from './models/PlayerRoleSchema';
|
||||||
export type { PlayerSchema } from './models/PlayerSchema';
|
export type { PlayerSchema } from './models/PlayerSchema';
|
||||||
export type { PlayerSchemaList } from './models/PlayerSchemaList';
|
|
||||||
export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvailabilityRoleSchema';
|
export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvailabilityRoleSchema';
|
||||||
export type { PutScheduleForm } from './models/PutScheduleForm';
|
export type { PutScheduleForm } from './models/PutScheduleForm';
|
||||||
export type { RoleSchema } from './models/RoleSchema';
|
export type { RoleSchema } from './models/RoleSchema';
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
/* generated using openapi-typescript-codegen -- do not edit */
|
|
||||||
/* istanbul ignore file */
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
import type { PlayerSchema } from './PlayerSchema';
|
|
||||||
export type GetUserResponse = {
|
|
||||||
isAdmin?: boolean;
|
|
||||||
realUser: (PlayerSchema | null);
|
|
||||||
steamId: string;
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type PlayerSchema = {
|
export type PlayerSchema = {
|
||||||
isAdmin?: boolean;
|
|
||||||
steamId: string;
|
steamId: string;
|
||||||
username: string;
|
username: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
/* generated using openapi-typescript-codegen -- do not edit */
|
|
||||||
/* istanbul ignore file */
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
import type { PlayerSchema } from './PlayerSchema';
|
|
||||||
export type PlayerSchemaList = Array<PlayerSchema>;
|
|
|
@ -6,7 +6,6 @@ import type { RoleSchema } from './RoleSchema';
|
||||||
export type ViewTeamMembersResponse = {
|
export type ViewTeamMembersResponse = {
|
||||||
availability: Array<number>;
|
availability: Array<number>;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
isAdmin?: boolean;
|
|
||||||
isTeamLeader?: boolean;
|
isTeamLeader?: boolean;
|
||||||
playtime: number;
|
playtime: number;
|
||||||
roles: Array<RoleSchema>;
|
roles: Array<RoleSchema>;
|
||||||
|
|
|
@ -12,10 +12,8 @@ import type { EventSchema } from '../models/EventSchema';
|
||||||
import type { EventWithPlayerSchema } from '../models/EventWithPlayerSchema';
|
import type { EventWithPlayerSchema } from '../models/EventWithPlayerSchema';
|
||||||
import type { EventWithPlayerSchemaList } from '../models/EventWithPlayerSchemaList';
|
import type { EventWithPlayerSchemaList } from '../models/EventWithPlayerSchemaList';
|
||||||
import type { GetEventPlayersResponse } from '../models/GetEventPlayersResponse';
|
import type { GetEventPlayersResponse } from '../models/GetEventPlayersResponse';
|
||||||
import type { GetUserResponse } from '../models/GetUserResponse';
|
|
||||||
import type { MatchSchema } from '../models/MatchSchema';
|
import type { MatchSchema } from '../models/MatchSchema';
|
||||||
import type { PlayerSchema } from '../models/PlayerSchema';
|
import type { PlayerSchema } from '../models/PlayerSchema';
|
||||||
import type { PlayerSchemaList } from '../models/PlayerSchemaList';
|
|
||||||
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
||||||
import type { SetUsernameJson } from '../models/SetUsernameJson';
|
import type { SetUsernameJson } from '../models/SetUsernameJson';
|
||||||
import type { SubmitMatchJson } from '../models/SubmitMatchJson';
|
import type { SubmitMatchJson } from '../models/SubmitMatchJson';
|
||||||
|
@ -281,10 +279,10 @@ export class DefaultService {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* get_user <GET>
|
* get_user <GET>
|
||||||
* @returns GetUserResponse OK
|
* @returns PlayerSchema OK
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public getUser(): CancelablePromise<GetUserResponse> {
|
public getUser(): CancelablePromise<PlayerSchema> {
|
||||||
return this.httpRequest.request({
|
return this.httpRequest.request({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/api/login/get-user',
|
url: '/api/login/get-user',
|
||||||
|
@ -520,6 +518,28 @@ export class DefaultService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* delete_team <DELETE>
|
||||||
|
* @param teamId
|
||||||
|
* @returns any OK
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public deleteTeam(
|
||||||
|
teamId: number,
|
||||||
|
): CancelablePromise<any> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/api/team/id/{team_id}/',
|
||||||
|
path: {
|
||||||
|
'team_id': teamId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
403: `Forbidden`,
|
||||||
|
404: `Not Found`,
|
||||||
|
422: `Unprocessable Content`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* view_team <GET>
|
* view_team <GET>
|
||||||
* @param teamId
|
* @param teamId
|
||||||
|
@ -781,54 +801,6 @@ export class DefaultService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* get_all_users <GET>
|
|
||||||
* @returns PlayerSchemaList OK
|
|
||||||
* @throws ApiError
|
|
||||||
*/
|
|
||||||
public getAllUsers(): CancelablePromise<PlayerSchemaList> {
|
|
||||||
return this.httpRequest.request({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/api/user/all',
|
|
||||||
errors: {
|
|
||||||
422: `Unprocessable Content`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* unset_doas <DELETE>
|
|
||||||
* @returns void
|
|
||||||
* @throws ApiError
|
|
||||||
*/
|
|
||||||
public unsetDoas(): CancelablePromise<void> {
|
|
||||||
return this.httpRequest.request({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: '/api/user/doas',
|
|
||||||
errors: {
|
|
||||||
422: `Unprocessable Content`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* set_doas <PUT>
|
|
||||||
* @param steamId
|
|
||||||
* @returns PlayerSchema OK
|
|
||||||
* @throws ApiError
|
|
||||||
*/
|
|
||||||
public setDoas(
|
|
||||||
steamId: string,
|
|
||||||
): CancelablePromise<PlayerSchema> {
|
|
||||||
return this.httpRequest.request({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/api/user/doas/{steam_id}',
|
|
||||||
path: {
|
|
||||||
'steam_id': steamId,
|
|
||||||
},
|
|
||||||
errors: {
|
|
||||||
422: `Unprocessable Content`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* set_username <POST>
|
* set_username <POST>
|
||||||
* @param requestBody
|
* @param requestBody
|
||||||
|
|
|
@ -19,9 +19,6 @@ function logout() {
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenuRoot>
|
<DropdownMenuRoot>
|
||||||
<DropdownMenuTrigger className="profile-button no-border">
|
<DropdownMenuTrigger className="profile-button no-border">
|
||||||
<span class="aside" v-if="authStore.realUser">
|
|
||||||
{{ authStore.realUser?.username }}, disguised as
|
|
||||||
</span>
|
|
||||||
{{ authStore.username }}
|
{{ authStore.username }}
|
||||||
<i class="bi bi-chevron-down" />
|
<i class="bi bi-chevron-down" />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
|
@ -43,14 +40,6 @@ function logout() {
|
||||||
</button>
|
</button>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem v-if="authStore.isAdmin">
|
|
||||||
<RouterLink class="button" :to="{ 'name': 'admin' }">
|
|
||||||
<button>
|
|
||||||
<i class="bi bi-person-check margin" />
|
|
||||||
Super secret admin stuff!
|
|
||||||
</button>
|
|
||||||
</RouterLink>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<button class="destructive" @click="logout">
|
<button class="destructive" @click="logout">
|
||||||
<i class="bi bi-box-arrow-right margin" />
|
<i class="bi bi-box-arrow-right margin" />
|
||||||
|
|
|
@ -13,9 +13,6 @@ import TeamSettingsIntegrationsView from "@/views/TeamSettings/IntegrationsView.
|
||||||
import TeamSettingsInvitesView from "@/views/TeamSettings/InvitesView.vue";
|
import TeamSettingsInvitesView from "@/views/TeamSettings/InvitesView.vue";
|
||||||
import TeamSettingsMatchesView from "@/views/TeamSettings/MatchesView.vue";
|
import TeamSettingsMatchesView from "@/views/TeamSettings/MatchesView.vue";
|
||||||
import UserSettingsView from "@/views/UserSettingsView.vue";
|
import UserSettingsView from "@/views/UserSettingsView.vue";
|
||||||
import AdminView from "@/views/AdminView.vue";
|
|
||||||
import AdminGeneralView from "@/views/Admin/GeneralView.vue";
|
|
||||||
import AdminDoasView from "@/views/Admin/DoasView.vue";
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
@ -87,23 +84,6 @@ const router = createRouter({
|
||||||
name: "user-settings",
|
name: "user-settings",
|
||||||
component: UserSettingsView,
|
component: UserSettingsView,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/admin",
|
|
||||||
name: "admin",
|
|
||||||
component: AdminView,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
name: "admin/",
|
|
||||||
component: AdminGeneralView,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "doas",
|
|
||||||
name: "admin/doas",
|
|
||||||
component: AdminDoasView,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,20 +2,18 @@ import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useClientStore } from "./client";
|
import { useClientStore } from "./client";
|
||||||
import { useRouter, type LocationQuery } from "vue-router";
|
import { useRouter, type LocationQuery } from "vue-router";
|
||||||
import { type GetUserResponse, type PlayerSchema } from "@/client";
|
import { type PlayerSchema } from "@/client";
|
||||||
|
|
||||||
export const useAuthStore = defineStore("auth", () => {
|
export const useAuthStore = defineStore("auth", () => {
|
||||||
const clientStore = useClientStore();
|
const clientStore = useClientStore();
|
||||||
const client = clientStore.client;
|
const client = clientStore.client;
|
||||||
|
|
||||||
const user = ref<GetUserResponse | null>(null);
|
const user = ref<PlayerSchema | null>(null);
|
||||||
const steamId = ref("");
|
const steamId = ref("");
|
||||||
const username = ref("");
|
const username = ref("");
|
||||||
const isLoggedIn = ref(false);
|
const isLoggedIn = ref(false);
|
||||||
const isRegistering = ref(false);
|
const isRegistering = ref(false);
|
||||||
const hasCheckedAuth = ref(false);
|
const hasCheckedAuth = ref(false);
|
||||||
const isAdmin = ref(false);
|
|
||||||
const realUser = ref<PlayerSchema | null>(null);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -37,9 +35,6 @@ export const useAuthStore = defineStore("auth", () => {
|
||||||
steamId.value = response.steamId;
|
steamId.value = response.steamId;
|
||||||
username.value = response.username;
|
username.value = response.username;
|
||||||
user.value = response;
|
user.value = response;
|
||||||
isAdmin.value = response.isAdmin || (response.realUser?.isAdmin ?? false);
|
|
||||||
realUser.value = response.realUser ?? null;
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
@ -81,48 +76,13 @@ export const useAuthStore = defineStore("auth", () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAllUsers() {
|
|
||||||
return client.default.getAllUsers();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setDoas(doasSteamId: string) {
|
|
||||||
return client.default.setDoas(doasSteamId)
|
|
||||||
.then((response) => {
|
|
||||||
if (user.value) {
|
|
||||||
realUser.value = {
|
|
||||||
steamId: user.value.steamId,
|
|
||||||
username: user.value.username,
|
|
||||||
isAdmin: user.value.isAdmin,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
steamId.value = response.steamId;
|
|
||||||
username.value = response.username;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unsetDoas() {
|
|
||||||
return client.default.unsetDoas()
|
|
||||||
.then((_) => {
|
|
||||||
if (realUser.value) {
|
|
||||||
steamId.value = realUser.value.steamId;
|
|
||||||
username.value = realUser.value.username;
|
|
||||||
}
|
|
||||||
realUser.value = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
steamId,
|
steamId,
|
||||||
username,
|
username,
|
||||||
isAdmin,
|
|
||||||
realUser,
|
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
hasCheckedAuth,
|
hasCheckedAuth,
|
||||||
isRegistering,
|
isRegistering,
|
||||||
getUser,
|
getUser,
|
||||||
getAllUsers,
|
|
||||||
setDoas,
|
|
||||||
unsetDoas,
|
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
setUsername,
|
setUsername,
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { type PlayerSchema, type PlayerSchemaList } from "@/client";
|
|
||||||
import { useAuthStore } from "@/stores/auth";
|
|
||||||
import { onMounted, ref } from "vue";
|
|
||||||
|
|
||||||
//const cookies = useCookies(["doas"], { doNotParse: true, autoUpdateDependencies: true }, universalCookie);
|
|
||||||
const doas = ref<PlayerSchema | undefined>();
|
|
||||||
const users = ref<PlayerSchemaList>([]);
|
|
||||||
const authStore = useAuthStore();
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
authStore.getAllUsers()
|
|
||||||
.then((response) => {
|
|
||||||
users.value = response;
|
|
||||||
//doas.value = response.find(user => user.steamId === cookies.get("doas"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function setDoas() {
|
|
||||||
if (doas.value) {
|
|
||||||
authStore.setDoas(doas.value.steamId);
|
|
||||||
} else {
|
|
||||||
authStore.unsetDoas();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDoas() {
|
|
||||||
doas.value = undefined;
|
|
||||||
authStore.unsetDoas();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h2>Become User</h2>
|
|
||||||
<p>
|
|
||||||
Do as/become a specific user.
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<div class="form-group margin">
|
|
||||||
<h3>User</h3>
|
|
||||||
<v-select
|
|
||||||
v-model="doas"
|
|
||||||
:options="users"
|
|
||||||
label="username"
|
|
||||||
placeholder="Select a user"
|
|
||||||
:clearable="true"
|
|
||||||
:searchable="true"
|
|
||||||
:close-on-select="true"
|
|
||||||
:show-search-input="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group margin">
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="destructive-on-hover" @click="removeDoas">
|
|
||||||
<i class="bi bi-trash" />
|
|
||||||
</button>
|
|
||||||
<button class="accent" @click="setDoas">
|
|
||||||
<i class="bi bi-check" />
|
|
||||||
Become {{ doas?.username }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h1>Admin Panel</h1>
|
|
||||||
<p>
|
|
||||||
This is the admin panel. Only admins can access this page.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
|
@ -1,22 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { RouterLink, RouterView } from "vue-router";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<main class="sidebar-container admin-panel">
|
|
||||||
<nav class="sidebar">
|
|
||||||
<div class="categories">
|
|
||||||
<h3>Admin Panel</h3>
|
|
||||||
<RouterLink class="tab" :to="{ name: 'admin/' }">
|
|
||||||
General
|
|
||||||
</RouterLink>
|
|
||||||
<RouterLink class="tab" :to="{ name: 'admin/doas' }">
|
|
||||||
Become User
|
|
||||||
</RouterLink>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div class="view">
|
|
||||||
<RouterView />
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
|
@ -23,7 +23,7 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="sidebar-container team-settings" v-if="team">
|
<main class="team-settings" v-if="team">
|
||||||
<nav class="sidebar">
|
<nav class="sidebar">
|
||||||
<div class="categories">
|
<div class="categories">
|
||||||
<div class="back-link">
|
<div class="back-link">
|
||||||
|
@ -59,7 +59,72 @@ onMounted(() => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.team-settings {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-settings nav.sidebar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-settings .view {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
.back-link {
|
.back-link {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.sidebar h3 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--overlay-0);
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 8pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar > .categories {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 192px;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar a.tab {
|
||||||
|
font-size: 11pt;
|
||||||
|
color: var(--overlay-0);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar a.tab:hover {
|
||||||
|
background-color: var(--crust);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar a.tab.router-link-exact-active {
|
||||||
|
background-color: var(--crust);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar button {
|
||||||
|
font-size: 11pt;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--overlay-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar button:hover {
|
||||||
|
background-color: var(--crust);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar button.destructive-on-hover:hover {
|
||||||
|
background-color: var(--destructive);
|
||||||
|
color: var(--base);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -21,36 +21,17 @@ STEAM_OPENID_URL = "https://steamcommunity.com/openid/login"
|
||||||
def index():
|
def index():
|
||||||
return "test"
|
return "test"
|
||||||
|
|
||||||
class GetUserResponse(PlayerSchema):
|
|
||||||
real_user: PlayerSchema | None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_model(cls, model: Player):
|
|
||||||
return GetUserResponse(
|
|
||||||
steam_id=str(model.steam_id),
|
|
||||||
username=model.username,
|
|
||||||
is_admin=model.is_admin,
|
|
||||||
real_user=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
@api_login.get("/get-user")
|
@api_login.get("/get-user")
|
||||||
@spec.validate(
|
@spec.validate(
|
||||||
resp=Response(
|
resp=Response(
|
||||||
HTTP_200=GetUserResponse,
|
HTTP_200=PlayerSchema,
|
||||||
HTTP_401=None,
|
HTTP_401=None,
|
||||||
),
|
),
|
||||||
operation_id="get_user"
|
operation_id="get_user"
|
||||||
)
|
)
|
||||||
@requires_authentication
|
@requires_authentication
|
||||||
def get_user(player: Player, auth_session: AuthSession):
|
def get_user(player: Player, auth_session: AuthSession):
|
||||||
if auth_session.player.steam_id != player.steam_id:
|
return PlayerSchema.from_model(player).dict(by_alias=True)
|
||||||
return GetUserResponse(
|
|
||||||
steam_id=str(player.steam_id),
|
|
||||||
username=player.username,
|
|
||||||
is_admin=player.is_admin,
|
|
||||||
real_user=PlayerSchema.from_model(auth_session.player)
|
|
||||||
).dict(by_alias=True)
|
|
||||||
return GetUserResponse.from_model(player).dict(by_alias=True)
|
|
||||||
|
|
||||||
@api_login.post("/authenticate")
|
@api_login.post("/authenticate")
|
||||||
def steam_authenticate():
|
def steam_authenticate():
|
||||||
|
|
|
@ -3,7 +3,6 @@ from typing import Optional
|
||||||
from flask import abort, make_response, request
|
from flask import abort, make_response, request
|
||||||
from sqlalchemy.sql.operators import json_path_getitem_op
|
from sqlalchemy.sql.operators import json_path_getitem_op
|
||||||
from app_db import db
|
from app_db import db
|
||||||
from models import auth_session
|
|
||||||
from models.auth_session import AuthSession
|
from models.auth_session import AuthSession
|
||||||
from models.player import Player
|
from models.player import Player
|
||||||
from models.player_team import PlayerTeam
|
from models.player_team import PlayerTeam
|
||||||
|
@ -14,7 +13,6 @@ def requires_authentication(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorator(*args, **kwargs):
|
def decorator(*args, **kwargs):
|
||||||
auth = request.cookies.get("auth")
|
auth = request.cookies.get("auth")
|
||||||
doas = request.cookies.get("doas")
|
|
||||||
|
|
||||||
if not auth:
|
if not auth:
|
||||||
abort(401)
|
abort(401)
|
||||||
|
@ -30,30 +28,6 @@ def requires_authentication(f):
|
||||||
player = auth_session.player
|
player = auth_session.player
|
||||||
kwargs["player"] = player
|
kwargs["player"] = player
|
||||||
kwargs["auth_session"] = auth_session
|
kwargs["auth_session"] = auth_session
|
||||||
|
|
||||||
if doas and player.is_admin:
|
|
||||||
doas_int = int(doas)
|
|
||||||
if doas_int and doas_int != player.steam_id:
|
|
||||||
doas_player = db.session.query(
|
|
||||||
Player
|
|
||||||
).where(
|
|
||||||
Player.steam_id == doas_int
|
|
||||||
).one_or_none()
|
|
||||||
|
|
||||||
if doas_player:
|
|
||||||
kwargs["player"] = doas_player
|
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
def requires_admin(f):
|
|
||||||
@wraps(f)
|
|
||||||
def decorator(*args, **kwargs):
|
|
||||||
auth_session: AuthSession | None = kwargs["auth_session"]
|
|
||||||
if not auth_session or not auth_session.player:
|
|
||||||
abort(401)
|
|
||||||
if not auth_session.player.is_admin:
|
|
||||||
abort(403)
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
"""Add is_admin field
|
|
||||||
|
|
||||||
Revision ID: f8588cdf998e
|
|
||||||
Revises: 3b18d4bfc6ac
|
|
||||||
Create Date: 2025-05-13 17:15:53.887878
|
|
||||||
|
|
||||||
"""
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = 'f8588cdf998e'
|
|
||||||
down_revision = '3b18d4bfc6ac'
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
with op.batch_alter_table('players', schema=None) as batch_op:
|
|
||||||
batch_op.add_column(sa.Column('is_admin', sa.Boolean(), nullable=False, default=False))
|
|
||||||
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
with op.batch_alter_table('players', schema=None) as batch_op:
|
|
||||||
batch_op.drop_column('is_admin')
|
|
||||||
|
|
||||||
# ### end Alembic commands ###
|
|
|
@ -12,7 +12,6 @@ class Player(app_db.BaseModel):
|
||||||
|
|
||||||
steam_id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
steam_id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
||||||
username: Mapped[str] = mapped_column(String(63))
|
username: Mapped[str] = mapped_column(String(63))
|
||||||
is_admin: Mapped[bool] = mapped_column(default=False)
|
|
||||||
|
|
||||||
teams: Mapped[list["PlayerTeam"]] = relationship(back_populates="player")
|
teams: Mapped[list["PlayerTeam"]] = relationship(back_populates="player")
|
||||||
auth_sessions: Mapped[list["AuthSession"]] = relationship(back_populates="player")
|
auth_sessions: Mapped[list["AuthSession"]] = relationship(back_populates="player")
|
||||||
|
@ -24,15 +23,10 @@ class Player(app_db.BaseModel):
|
||||||
class PlayerSchema(spec.BaseModel):
|
class PlayerSchema(spec.BaseModel):
|
||||||
steam_id: str
|
steam_id: str
|
||||||
username: str
|
username: str
|
||||||
is_admin: bool = False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_model(cls, player: Player):
|
def from_model(cls, player: Player):
|
||||||
return cls(
|
return cls(steam_id=str(player.steam_id), username=player.username)
|
||||||
steam_id=str(player.steam_id),
|
|
||||||
username=player.username,
|
|
||||||
is_admin=player.is_admin
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
from models.auth_session import AuthSession
|
from models.auth_session import AuthSession
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from flask import Blueprint, abort, make_response
|
from flask import Blueprint
|
||||||
from spectree import Response
|
from spectree import Response
|
||||||
from middleware import requires_admin, requires_authentication
|
from middleware import requires_authentication
|
||||||
from models.player import Player, PlayerSchema
|
from models.player import Player, PlayerSchema
|
||||||
from spec import spec, BaseModel
|
from spec import spec, BaseModel
|
||||||
from app_db import db
|
from app_db import db
|
||||||
|
@ -23,53 +23,3 @@ def set_username(json: SetUsernameJson, player: Player, **kwargs):
|
||||||
player.username = json.username
|
player.username = json.username
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return PlayerSchema.from_model(player).dict(by_alias=True), 200
|
return PlayerSchema.from_model(player).dict(by_alias=True), 200
|
||||||
|
|
||||||
@api_user.get("/all")
|
|
||||||
@spec.validate(
|
|
||||||
resp=Response(
|
|
||||||
HTTP_200=list[PlayerSchema],
|
|
||||||
),
|
|
||||||
operation_id="get_all_users",
|
|
||||||
)
|
|
||||||
@requires_authentication
|
|
||||||
@requires_admin
|
|
||||||
def get_all_users(player: Player, **kwargs):
|
|
||||||
players = db.session.query(Player).all()
|
|
||||||
return list(map(lambda p: PlayerSchema.from_model(p).dict(by_alias=True), players)), 200
|
|
||||||
|
|
||||||
@api_user.put("/doas/<steam_id>")
|
|
||||||
@spec.validate(
|
|
||||||
resp=Response(
|
|
||||||
HTTP_200=PlayerSchema,
|
|
||||||
),
|
|
||||||
operation_id="set_doas"
|
|
||||||
)
|
|
||||||
@requires_authentication
|
|
||||||
@requires_admin
|
|
||||||
def set_doas(steam_id: str, **_):
|
|
||||||
player = db.session.query(Player).where(
|
|
||||||
Player.steam_id == steam_id
|
|
||||||
).one_or_none()
|
|
||||||
|
|
||||||
if not player:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
resp = make_response(
|
|
||||||
PlayerSchema.from_model(player).dict(by_alias=True)
|
|
||||||
)
|
|
||||||
resp.set_cookie("doas", steam_id, httponly=True)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
@api_user.delete("/doas")
|
|
||||||
@spec.validate(
|
|
||||||
resp=Response(
|
|
||||||
HTTP_204=None,
|
|
||||||
),
|
|
||||||
operation_id="unset_doas"
|
|
||||||
)
|
|
||||||
@requires_authentication
|
|
||||||
@requires_admin
|
|
||||||
def unset_doas(**_):
|
|
||||||
resp = make_response({ }, 204)
|
|
||||||
resp.delete_cookie("doas", httponly=True)
|
|
||||||
return resp
|
|
||||||
|
|
Loading…
Reference in New Issue