Make registration RESTful

master
John Montagu, the 4th Earl of Sandvich 2024-11-15 19:37:41 -08:00
parent 6821447c74
commit afba73e1e8
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
13 changed files with 142 additions and 65 deletions

View File

@ -18,6 +18,7 @@ export type { PlayerSchema } from './models/PlayerSchema';
export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvailabilityRoleSchema';
export type { PutScheduleForm } from './models/PutScheduleForm';
export type { RoleSchema } from './models/RoleSchema';
export type { SetUsernameJson } from './models/SetUsernameJson';
export type { TeamInviteSchema } from './models/TeamInviteSchema';
export type { TeamInviteSchemaList } from './models/TeamInviteSchemaList';
export { TeamRole } from './models/TeamRole';

View File

@ -5,6 +5,7 @@
export type CreateTeamJson = {
discordWebhookUrl?: string;
leagueTimezone: string;
minuteOffset?: number;
teamName: string;
};

View File

@ -0,0 +1,8 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type SetUsernameJson = {
username: string;
};

View File

@ -3,7 +3,7 @@
/* tslint:disable */
/* eslint-disable */
export type TeamSchema = {
discordWebhookUrl?: string;
createdAt: string;
id: number;
minuteOffset: number;
teamName: string;

View File

@ -7,6 +7,7 @@ import type { CreateTeamJson } from '../models/CreateTeamJson';
import type { EditMemberRolesJson } from '../models/EditMemberRolesJson';
import type { PlayerSchema } from '../models/PlayerSchema';
import type { PutScheduleForm } from '../models/PutScheduleForm';
import type { SetUsernameJson } from '../models/SetUsernameJson';
import type { TeamInviteSchema } from '../models/TeamInviteSchema';
import type { TeamInviteSchemaList } from '../models/TeamInviteSchemaList';
import type { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
@ -455,4 +456,23 @@ export class DefaultService {
},
});
}
/**
* set_username <POST>
* @param requestBody
* @returns PlayerSchema OK
* @throws ApiError
*/
public setUsername(
requestBody?: SetUsernameJson,
): CancelablePromise<PlayerSchema> {
return this.httpRequest.request({
method: 'POST',
url: '/api/user/username',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Unprocessable Entity`,
},
});
}
}

View File

@ -38,7 +38,11 @@ const router = createRouter({
{
path: "/team/id/:id",
name: "team-details",
component: TeamDetailsView
component: TeamDetailsView,
//children: [
// path: "members",
// component:
//],
},
]
});
@ -49,10 +53,12 @@ router
const authStore = useAuthStore();
console.log("test");
if (!authStore.isLoggedIn && !authStore.hasCheckedAuth) {
try {
await authStore.getUser();
} catch (exception) {
if (to.name != "login") {
try {
await authStore.getUser();
} catch (exception) {
}
}
}
});

View File

@ -46,6 +46,10 @@ export const useAuthStore = defineStore("auth", () => {
});
}
async function setUsername(username: string) {
return client.default.setUsername({ username });
}
return {
steamId,
username,
@ -54,5 +58,6 @@ export const useAuthStore = defineStore("auth", () => {
isRegistering,
getUser,
login,
setUsername,
}
});

View File

@ -65,11 +65,11 @@ export const useTeamsStore = defineStore("teams", () => {
);
}
async function createTeam(teamName: string, tz: string, webhook?: string) {
async function createTeam(teamName: string, tz: string, minuteOffset: number) {
return await client.default.createTeam({
teamName,
leagueTimezone: tz,
discordWebhookUrl: webhook,
minuteOffset,
});
}

View File

@ -1,48 +1,75 @@
<script setup lang="ts">
import { useAuthStore } from "../stores/auth";
import { onMounted, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const queryParams = route.query;
const queryParams = computed(() => route.query);
const auth = useAuthStore();
const registerUsername = ref("");
function register() {
const params = {
...queryParams,
username: registerUsername,
}
//const params = {
// ...queryParams.value,
// username: registerUsername.value,
//};
auth.login(params)
//auth.login(params)
// .then(() => router.push("/"));
auth.setUsername(registerUsername.value)
.then(() => router.push("/"));
}
onMounted(() => {
if (Object.keys(queryParams).length == 0) {
auth.isRegistering = true;
return;
}
auth.login(queryParams)
.then(() => router.push("/"));
auth.login(queryParams.value)
.then(() => {
if (!auth.isRegistering) {
router.push("/");
}
});
});
</script>
<template>
<div>
<main>
<main>
<div class="login-container">
<template v-if="auth.isRegistering">
<h1>Register</h1>
<input v-model="registerUsername" />
<button class="accent" type="submit">Register</button>
<h1>New account</h1>
<p>
Your account has been newly created. Select a username to be
associated with this account.
</p>
<div class="form-group margin">
<h3>Username</h3>
<input v-model="registerUsername" />
</div>
<div class="form-group margin">
<div class="action-buttons">
<button class="accent" type="submit" @click="register()">
Save
</button>
</div>
</div>
</template>
<div v-else>
Logging in...
</div>
</main>
</div>
</div>
</main>
</template>
<style scoped>
.login-container {
align-items: center;
max-width: 500px;
margin: auto;
}
h3 {
font-size: 11pt;
font-weight: 700;
}
</style>

View File

@ -24,7 +24,7 @@ watch(minuteOffset, (newValue) => {
const webhook = ref("");
function createTeam() {
teams.createTeam(teamName.value, timezone.value, webhook.value)
teams.createTeam(teamName.value, timezone.value, minuteOffset.value)
.then(() => {
router.push("/");
});
@ -64,7 +64,7 @@ function createTeam() {
</div>
</div>
<em class="aside">
Matches will be scheduled against {{ timezone }} at
Matches will be scheduled based on {{ timezone }} at
{{ minuteOffset }}
<span v-if="minuteOffset == 1">
minute
@ -107,23 +107,6 @@ function createTeam() {
font-size: 9pt;
}
.form-group {
display: flex;
flex-direction: column;
gap: 4px;
flex-grow: 1;
}
.form-group.margin {
margin-top: 16px;
margin-bottom: 16px;
}
.form-group.row {
flex-direction: row;
margin: none;
}
#minute-offset-group {
flex-grow: unset;
flex-shrink: 1;
@ -135,9 +118,4 @@ input {
width: 100%;
color: var(--text);
}
.action-buttons {
display: flex;
justify-content: end;
}
</style>

View File

@ -5,6 +5,7 @@ import login
import schedule
import team
from spec import spec
import user
connect_db_with_app()
@ -12,6 +13,7 @@ api = Blueprint("api", __name__, url_prefix="/api")
api.register_blueprint(login.api_login)
api.register_blueprint(schedule.api_schedule)
api.register_blueprint(team.api_team)
api.register_blueprint(user.api_user)
@api.get("/debug/set-cookie")
@api.post("/debug/set-cookie")

View File

@ -11,6 +11,7 @@ from app_db import db
from models.auth_session import AuthSession
from models.player import Player, PlayerSchema
from middleware import requires_authentication
import sys
api_login = Blueprint("login", __name__, url_prefix="/login")
@ -36,7 +37,14 @@ def get_user(player: Player, auth_session: AuthSession):
def steam_authenticate():
params = request.get_json()
params["openid.mode"] = "check_authentication"
response = requests.post(STEAM_OPENID_URL, data=params)
steam_params = params
if "username" in steam_params:
del steam_params["username"]
response = requests.post(STEAM_OPENID_URL, data=steam_params)
print("response text = ", file=sys.stderr)
print(response.text, file=sys.stderr)
# check if authentication was successful
if "is_valid:true" in response.text:
@ -50,19 +58,14 @@ def steam_authenticate():
Player.steam_id == steam_id
).one_or_none()
is_registering = False
if not player:
if "username" in params:
# we are registering, so create user
player = Player()
player.username = params["username"]
player.steam_id = steam_id
else:
# prompt client to resend with username field
return make_response({
"message": "Awaiting registration",
"hint": "Resend the POST request with a username field",
"isRegistering": True,
})
# we are registering, so create user
player = Player()
player.username = str(steam_id)
player.steam_id = steam_id
is_registering = True
db.session.add(player)
auth_session = create_auth_session_for_player(player)
@ -70,6 +73,7 @@ def steam_authenticate():
"message": "Logged in",
"steamId": player.steam_id,
"username": player.username,
"isRegistering": is_registering,
})
# TODO: secure=True in production

View File

@ -0,0 +1,25 @@
from flask import Blueprint
from spectree import Response
from middleware import requires_authentication
from models.player import Player, PlayerSchema
from spec import spec, BaseModel
from app_db import db
api_user = Blueprint("user", __name__, url_prefix="/user")
class SetUsernameJson(BaseModel):
username: str
@api_user.post("username")
@spec.validate(
resp=Response(
HTTP_200=PlayerSchema,
),
operation_id="set_username",
)
@requires_authentication
def set_username(json: SetUsernameJson, player: Player, **kwargs):
player.username = json.username
db.session.commit()
return PlayerSchema.from_model(player).dict(by_alias=True), 200