mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
signups
This commit is contained in:
parent
eea356e5c1
commit
f22522a52c
15 changed files with 934 additions and 242 deletions
|
@ -86,6 +86,8 @@ export default {
|
||||||
search: false,
|
search: false,
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
|
// For Later!
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if 'system' is set for dark mode and then sets the corrisponding value for vuetify
|
* Checks if 'system' is set for dark mode and then sets the corrisponding value for vuetify
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,8 +7,8 @@ import migration from "./migration";
|
||||||
import myUtils from "./upload";
|
import myUtils from "./upload";
|
||||||
import category from "./category";
|
import category from "./category";
|
||||||
import meta from "./meta";
|
import meta from "./meta";
|
||||||
import users from "./users"
|
import users from "./users";
|
||||||
|
import signUps from "./signUps";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
recipes: recipe,
|
recipes: recipe,
|
||||||
|
@ -20,5 +20,6 @@ export default {
|
||||||
utils: myUtils,
|
utils: myUtils,
|
||||||
categories: category,
|
categories: category,
|
||||||
meta: meta,
|
meta: meta,
|
||||||
users: users
|
users: users,
|
||||||
|
signUps: signUps,
|
||||||
};
|
};
|
||||||
|
|
30
frontend/src/api/signUps.js
Normal file
30
frontend/src/api/signUps.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { baseURL } from "./api-utils";
|
||||||
|
import { apiReq } from "./api-utils";
|
||||||
|
|
||||||
|
const signUpPrefix = baseURL + "users/sign-ups";
|
||||||
|
|
||||||
|
const signUpURLs = {
|
||||||
|
all: `${signUpPrefix}`,
|
||||||
|
createToken: `${signUpPrefix}`,
|
||||||
|
deleteToken: token => `${signUpPrefix}/${token}`,
|
||||||
|
createUser: token => `${signUpPrefix}/${token}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async getAll() {
|
||||||
|
let response = await apiReq.get(signUpURLs.all);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
async createToken(data) {
|
||||||
|
let response = await apiReq.post(signUpURLs.createToken, data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
async deleteToken(token) {
|
||||||
|
let response = await apiReq.delete(signUpURLs.deleteToken(token));
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
async createUser(token, data) {
|
||||||
|
let response = await apiReq.post(signUpURLs.createUser(token), data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
};
|
|
@ -7,6 +7,7 @@ const authURLs = {
|
||||||
token: `${authPrefix}/token`,
|
token: `${authPrefix}/token`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const usersURLs = {
|
const usersURLs = {
|
||||||
users: `${userPrefix}`,
|
users: `${userPrefix}`,
|
||||||
self: `${userPrefix}/self`,
|
self: `${userPrefix}/self`,
|
||||||
|
|
252
frontend/src/components/Admin/ManageUsers/TheGroupTable.vue
Normal file
252
frontend/src/components/Admin/ManageUsers/TheGroupTable.vue
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
<template>
|
||||||
|
<v-card outlined>
|
||||||
|
<Confirmation
|
||||||
|
ref="deleteUserDialog"
|
||||||
|
title="Confirm User Deletion"
|
||||||
|
:message="
|
||||||
|
`Are you sure you want to delete the user <b>${activeName} ID: ${activeId}<b/>`
|
||||||
|
"
|
||||||
|
icon="mdi-alert"
|
||||||
|
@confirm="deleteUser"
|
||||||
|
:width="450"
|
||||||
|
@close="closeDelete"
|
||||||
|
/>
|
||||||
|
<v-toolbar flat>
|
||||||
|
<v-icon large color="accent" class="mr-1">
|
||||||
|
mdi-account-group
|
||||||
|
</v-icon>
|
||||||
|
<v-toolbar-title class="headine">
|
||||||
|
User Groups
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer> </v-spacer>
|
||||||
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
|
<template v-slot:activator="{ on, attrs }">
|
||||||
|
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
||||||
|
Create Group
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-card>
|
||||||
|
<v-app-bar dark dense color="primary">
|
||||||
|
<v-icon left>
|
||||||
|
mdi-account
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
<v-toolbar-title class="headline">
|
||||||
|
{{ formTitle }}
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-toolbar-title class="headline">
|
||||||
|
User ID: {{ editedItem.id }}
|
||||||
|
</v-toolbar-title>
|
||||||
|
</v-app-bar>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="newUser">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.fullName"
|
||||||
|
label="Full Name"
|
||||||
|
:rules="[existsRule]"
|
||||||
|
validate-on-blur
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.email"
|
||||||
|
label="Email"
|
||||||
|
:rules="[existsRule, emailRule]"
|
||||||
|
validate-on-blur
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.family"
|
||||||
|
label="Family Group"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.password"
|
||||||
|
label="User Password"
|
||||||
|
:rules="[existsRule, minRule]"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="3">
|
||||||
|
<v-switch v-model="editedItem.admin" label="Admin"></v-switch>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="grey" text @click="close">
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="primary" @click="save">
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-toolbar>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<v-data-table :headers="headers" :items="users" sort-by="calories">
|
||||||
|
<template v-slot:item.actions="{ item }">
|
||||||
|
<v-btn class="mr-1" small color="error" @click="deleteItem(item)">
|
||||||
|
<v-icon small left>
|
||||||
|
mdi-delete
|
||||||
|
</v-icon>
|
||||||
|
Delete
|
||||||
|
</v-btn>
|
||||||
|
<v-btn small color="success" @click="editItem(item)">
|
||||||
|
<v-icon small left class="mr-2">
|
||||||
|
mdi-pencil
|
||||||
|
</v-icon>
|
||||||
|
Edit
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.admin="{ item }">
|
||||||
|
{{ item.admin ? "Admin" : "User" }}
|
||||||
|
</template>
|
||||||
|
<template v-slot:no-data>
|
||||||
|
<v-btn color="primary" @click="initialize">
|
||||||
|
Reset
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Confirmation from "@/components/UI/Confirmation";
|
||||||
|
import api from "@/api";
|
||||||
|
import { validators } from "@/mixins/validators";
|
||||||
|
export default {
|
||||||
|
components: { Confirmation },
|
||||||
|
mixins: [validators],
|
||||||
|
data: () => ({
|
||||||
|
dialog: false,
|
||||||
|
activeId: null,
|
||||||
|
activeName: null,
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
text: "User ID",
|
||||||
|
align: "start",
|
||||||
|
sortable: false,
|
||||||
|
value: "id",
|
||||||
|
},
|
||||||
|
{ text: "Full Name", value: "fullName" },
|
||||||
|
{ text: "Email", value: "email" },
|
||||||
|
{ text: "Family", value: "family" },
|
||||||
|
{ text: "Admin", value: "admin" },
|
||||||
|
{ text: "", value: "actions", sortable: false, align: "center" },
|
||||||
|
],
|
||||||
|
users: [],
|
||||||
|
editedIndex: -1,
|
||||||
|
editedItem: {
|
||||||
|
id: 0,
|
||||||
|
fullName: "",
|
||||||
|
password: "",
|
||||||
|
email: "",
|
||||||
|
family: "",
|
||||||
|
admin: false,
|
||||||
|
},
|
||||||
|
defaultItem: {
|
||||||
|
id: 0,
|
||||||
|
fullName: "",
|
||||||
|
password: "",
|
||||||
|
email: "",
|
||||||
|
family: "",
|
||||||
|
admin: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
formTitle() {
|
||||||
|
return this.editedIndex === -1 ? "New User" : "Edit User";
|
||||||
|
},
|
||||||
|
showPassword() {
|
||||||
|
return this.editedIndex === -1 ? true : false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
dialog(val) {
|
||||||
|
val || this.close();
|
||||||
|
},
|
||||||
|
dialogDelete(val) {
|
||||||
|
val || this.closeDelete();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async initialize() {
|
||||||
|
this.users = await api.users.allUsers();
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteUser() {
|
||||||
|
await api.users.delete(this.activeId);
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
editItem(item) {
|
||||||
|
this.editedIndex = this.users.indexOf(item);
|
||||||
|
this.editedItem = Object.assign({}, item);
|
||||||
|
this.dialog = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItem(item) {
|
||||||
|
this.activeId = item.id;
|
||||||
|
this.activeName = item.fullName;
|
||||||
|
this.editedIndex = this.users.indexOf(item);
|
||||||
|
this.editedItem = Object.assign({}, item);
|
||||||
|
this.$refs.deleteUserDialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItemConfirm() {
|
||||||
|
this.users.splice(this.editedIndex, 1);
|
||||||
|
this.closeDelete();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.dialog = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.editedItem = Object.assign({}, this.defaultItem);
|
||||||
|
this.editedIndex = -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDelete() {
|
||||||
|
this.dialogDelete = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.editedItem = Object.assign({}, this.defaultItem);
|
||||||
|
this.editedIndex = -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
if (this.editedIndex > -1) {
|
||||||
|
api.users.update(this.editedItem);
|
||||||
|
this.close();
|
||||||
|
} else if (this.$refs.newUser.validate()) {
|
||||||
|
api.users.create(this.editedItem);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
await this.initialize();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
218
frontend/src/components/Admin/ManageUsers/TheSignUpTable.vue
Normal file
218
frontend/src/components/Admin/ManageUsers/TheSignUpTable.vue
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
<template>
|
||||||
|
<v-card outlined>
|
||||||
|
<Confirmation
|
||||||
|
ref="deleteUserDialog"
|
||||||
|
title="Confirm User Deletion"
|
||||||
|
:message="`Are you sure you want to delete the link <b>${activeName}<b/>`"
|
||||||
|
icon="mdi-alert"
|
||||||
|
@confirm="deleteUser"
|
||||||
|
:width="450"
|
||||||
|
@close="closeDelete"
|
||||||
|
/>
|
||||||
|
<v-toolbar flat>
|
||||||
|
<v-icon large color="accent" class="mr-1">
|
||||||
|
mdi-link-variant
|
||||||
|
</v-icon>
|
||||||
|
<v-toolbar-title class="headine">
|
||||||
|
Sign Up Links
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer> </v-spacer>
|
||||||
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
|
<template v-slot:activator="{ on, attrs }">
|
||||||
|
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
||||||
|
Create Link
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-card>
|
||||||
|
<v-app-bar dark dense color="primary">
|
||||||
|
<v-icon left>
|
||||||
|
mdi-account
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
<v-toolbar-title class="headline">
|
||||||
|
Create Link
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-app-bar>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="newUser">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.name"
|
||||||
|
label="Link Name"
|
||||||
|
:rules="[existsRule]"
|
||||||
|
validate-on-blur
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="grey" text @click="close">
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="primary" @click="save">
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-toolbar>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<v-data-table :headers="headers" :items="links" sort-by="calories">
|
||||||
|
<template v-slot:item.token="{ item }">
|
||||||
|
{{ `${baseURL}/user/sign-up/${item.token}` }}
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
class="mr-1"
|
||||||
|
small
|
||||||
|
color="accent"
|
||||||
|
@click="updateClipboard(`${baseURL}/user/sign-up/${item.token}`)"
|
||||||
|
>
|
||||||
|
<v-icon>
|
||||||
|
mdi-content-copy
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.actions="{ item }">
|
||||||
|
<v-btn class="mr-1" small color="error" @click="deleteItem(item)">
|
||||||
|
<v-icon small left>
|
||||||
|
mdi-delete
|
||||||
|
</v-icon>
|
||||||
|
Delete
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Confirmation from "@/components/UI/Confirmation";
|
||||||
|
import api from "@/api";
|
||||||
|
import { validators } from "@/mixins/validators";
|
||||||
|
export default {
|
||||||
|
components: { Confirmation },
|
||||||
|
mixins: [validators],
|
||||||
|
data: () => ({
|
||||||
|
dialog: false,
|
||||||
|
activeId: null,
|
||||||
|
activeName: null,
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
text: "Link ID",
|
||||||
|
align: "start",
|
||||||
|
sortable: false,
|
||||||
|
value: "id",
|
||||||
|
},
|
||||||
|
{ text: "Name", value: "name" },
|
||||||
|
{ text: "Token", value: "token" },
|
||||||
|
{ text: "", value: "actions", sortable: false, align: "center" },
|
||||||
|
],
|
||||||
|
links: [],
|
||||||
|
editedIndex: -1,
|
||||||
|
editedItem: {
|
||||||
|
name: "",
|
||||||
|
token: "",
|
||||||
|
id: 0,
|
||||||
|
},
|
||||||
|
defaultItem: {
|
||||||
|
name: "",
|
||||||
|
token: "",
|
||||||
|
id: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
baseURL() {
|
||||||
|
return window.location.origin;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
dialog(val) {
|
||||||
|
val || this.close();
|
||||||
|
},
|
||||||
|
dialogDelete(val) {
|
||||||
|
val || this.closeDelete();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateClipboard(newClip) {
|
||||||
|
navigator.clipboard.writeText(newClip).then(
|
||||||
|
function() {
|
||||||
|
console.log("Copied", newClip);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
console.log("Copy Failed", newClip);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async initialize() {
|
||||||
|
this.links = await api.signUps.getAll();
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteUser() {
|
||||||
|
await api.signUps.deleteToken(this.activeId);
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
editItem(item) {
|
||||||
|
this.editedIndex = this.links.indexOf(item);
|
||||||
|
this.editedItem = Object.assign({}, item);
|
||||||
|
this.dialog = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItem(item) {
|
||||||
|
this.activeId = item.token;
|
||||||
|
this.activeName = item.name;
|
||||||
|
this.editedIndex = this.links.indexOf(item);
|
||||||
|
this.editedItem = Object.assign({}, item);
|
||||||
|
this.$refs.deleteUserDialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItemConfirm() {
|
||||||
|
this.links.splice(this.editedIndex, 1);
|
||||||
|
this.closeDelete();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.dialog = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.editedItem = Object.assign({}, this.defaultItem);
|
||||||
|
this.editedIndex = -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDelete() {
|
||||||
|
this.dialogDelete = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.editedItem = Object.assign({}, this.defaultItem);
|
||||||
|
this.editedIndex = -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
if (this.editedIndex > -1) {
|
||||||
|
api.links.update(this.editedItem);
|
||||||
|
this.close();
|
||||||
|
} else if (this.$refs.newUser.validate()) {
|
||||||
|
api.signUps.createToken({ name: this.editedItem.name });
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
await this.initialize();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
252
frontend/src/components/Admin/ManageUsers/TheUserTable.vue
Normal file
252
frontend/src/components/Admin/ManageUsers/TheUserTable.vue
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
<template>
|
||||||
|
<v-card outlined>
|
||||||
|
<Confirmation
|
||||||
|
ref="deleteUserDialog"
|
||||||
|
title="Confirm User Deletion"
|
||||||
|
:message="
|
||||||
|
`Are you sure you want to delete the user <b>${activeName} ID: ${activeId}<b/>`
|
||||||
|
"
|
||||||
|
icon="mdi-alert"
|
||||||
|
@confirm="deleteUser"
|
||||||
|
:width="450"
|
||||||
|
@close="closeDelete"
|
||||||
|
/>
|
||||||
|
<v-toolbar flat>
|
||||||
|
<v-icon large color="accent" class="mr-1">
|
||||||
|
mdi-account
|
||||||
|
</v-icon>
|
||||||
|
<v-toolbar-title class="headine">
|
||||||
|
Users
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer> </v-spacer>
|
||||||
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
|
<template v-slot:activator="{ on, attrs }">
|
||||||
|
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
||||||
|
Create User
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-card>
|
||||||
|
<v-app-bar dark dense color="primary">
|
||||||
|
<v-icon left>
|
||||||
|
mdi-account
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
<v-toolbar-title class="headline">
|
||||||
|
{{ formTitle }}
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-toolbar-title class="headline">
|
||||||
|
User ID: {{ editedItem.id }}
|
||||||
|
</v-toolbar-title>
|
||||||
|
</v-app-bar>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="newUser">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.fullName"
|
||||||
|
label="Full Name"
|
||||||
|
:rules="[existsRule]"
|
||||||
|
validate-on-blur
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.email"
|
||||||
|
label="Email"
|
||||||
|
:rules="[existsRule, emailRule]"
|
||||||
|
validate-on-blur
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.family"
|
||||||
|
label="Family Group"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
||||||
|
<v-text-field
|
||||||
|
v-model="editedItem.password"
|
||||||
|
label="User Password"
|
||||||
|
:rules="[existsRule, minRule]"
|
||||||
|
></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" md="3">
|
||||||
|
<v-switch v-model="editedItem.admin" label="Admin"></v-switch>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="grey" text @click="close">
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="primary" @click="save">
|
||||||
|
Save
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-toolbar>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<v-data-table :headers="headers" :items="users" sort-by="calories">
|
||||||
|
<template v-slot:item.actions="{ item }">
|
||||||
|
<v-btn class="mr-1" small color="error" @click="deleteItem(item)">
|
||||||
|
<v-icon small left>
|
||||||
|
mdi-delete
|
||||||
|
</v-icon>
|
||||||
|
Delete
|
||||||
|
</v-btn>
|
||||||
|
<v-btn small color="success" @click="editItem(item)">
|
||||||
|
<v-icon small left class="mr-2">
|
||||||
|
mdi-pencil
|
||||||
|
</v-icon>
|
||||||
|
Edit
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.admin="{ item }">
|
||||||
|
{{ item.admin ? "Admin" : "User" }}
|
||||||
|
</template>
|
||||||
|
<template v-slot:no-data>
|
||||||
|
<v-btn color="primary" @click="initialize">
|
||||||
|
Reset
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Confirmation from "@/components/UI/Confirmation";
|
||||||
|
import api from "@/api";
|
||||||
|
import { validators } from "@/mixins/validators";
|
||||||
|
export default {
|
||||||
|
components: { Confirmation },
|
||||||
|
mixins: [validators],
|
||||||
|
data: () => ({
|
||||||
|
dialog: false,
|
||||||
|
activeId: null,
|
||||||
|
activeName: null,
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
text: "User ID",
|
||||||
|
align: "start",
|
||||||
|
sortable: false,
|
||||||
|
value: "id",
|
||||||
|
},
|
||||||
|
{ text: "Full Name", value: "fullName" },
|
||||||
|
{ text: "Email", value: "email" },
|
||||||
|
{ text: "Family", value: "family" },
|
||||||
|
{ text: "Admin", value: "admin" },
|
||||||
|
{ text: "", value: "actions", sortable: false, align: "center" },
|
||||||
|
],
|
||||||
|
users: [],
|
||||||
|
editedIndex: -1,
|
||||||
|
editedItem: {
|
||||||
|
id: 0,
|
||||||
|
fullName: "",
|
||||||
|
password: "",
|
||||||
|
email: "",
|
||||||
|
family: "",
|
||||||
|
admin: false,
|
||||||
|
},
|
||||||
|
defaultItem: {
|
||||||
|
id: 0,
|
||||||
|
fullName: "",
|
||||||
|
password: "",
|
||||||
|
email: "",
|
||||||
|
family: "",
|
||||||
|
admin: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
formTitle() {
|
||||||
|
return this.editedIndex === -1 ? "New User" : "Edit User";
|
||||||
|
},
|
||||||
|
showPassword() {
|
||||||
|
return this.editedIndex === -1 ? true : false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
dialog(val) {
|
||||||
|
val || this.close();
|
||||||
|
},
|
||||||
|
dialogDelete(val) {
|
||||||
|
val || this.closeDelete();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async initialize() {
|
||||||
|
this.users = await api.users.allUsers();
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteUser() {
|
||||||
|
await api.users.delete(this.activeId);
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
editItem(item) {
|
||||||
|
this.editedIndex = this.users.indexOf(item);
|
||||||
|
this.editedItem = Object.assign({}, item);
|
||||||
|
this.dialog = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItem(item) {
|
||||||
|
this.activeId = item.id;
|
||||||
|
this.activeName = item.fullName;
|
||||||
|
this.editedIndex = this.users.indexOf(item);
|
||||||
|
this.editedItem = Object.assign({}, item);
|
||||||
|
this.$refs.deleteUserDialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItemConfirm() {
|
||||||
|
this.users.splice(this.editedIndex, 1);
|
||||||
|
this.closeDelete();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.dialog = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.editedItem = Object.assign({}, this.defaultItem);
|
||||||
|
this.editedIndex = -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDelete() {
|
||||||
|
this.dialogDelete = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.editedItem = Object.assign({}, this.defaultItem);
|
||||||
|
this.editedIndex = -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
if (this.editedIndex > -1) {
|
||||||
|
api.users.update(this.editedItem);
|
||||||
|
this.close();
|
||||||
|
} else if (this.$refs.newUser.validate()) {
|
||||||
|
api.users.create(this.editedItem);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
await this.initialize();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -1,249 +1,56 @@
|
||||||
<template>
|
<template>
|
||||||
<v-data-table
|
<div>
|
||||||
:headers="headers"
|
<v-card flat>
|
||||||
:items="users"
|
<v-tabs
|
||||||
sort-by="calories"
|
v-model="tab"
|
||||||
class="elevation-1"
|
background-color="primary"
|
||||||
>
|
centered
|
||||||
<template v-slot:top>
|
dark
|
||||||
<v-toolbar flat>
|
icons-and-text
|
||||||
<v-toolbar-title>Mealie Users</v-toolbar-title>
|
>
|
||||||
<v-divider class="mx-4" inset vertical></v-divider>
|
<v-tabs-slider></v-tabs-slider>
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
|
||||||
<template v-slot:activator="{ on, attrs }">
|
|
||||||
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">
|
|
||||||
Create User
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
<v-card>
|
|
||||||
<v-app-bar dark dense color="primary">
|
|
||||||
<v-icon left>
|
|
||||||
mdi-account
|
|
||||||
</v-icon>
|
|
||||||
|
|
||||||
<v-toolbar-title class="headline">
|
<v-tab>
|
||||||
{{ formTitle }}
|
Users
|
||||||
</v-toolbar-title>
|
<v-icon>mdi-account</v-icon>
|
||||||
|
</v-tab>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-tab>
|
||||||
<v-toolbar-title class="headline">
|
Sign-Up Links
|
||||||
User ID: {{ editedItem.id }}
|
<v-icon>mdi-account-plus-outline</v-icon>
|
||||||
</v-toolbar-title>
|
</v-tab>
|
||||||
</v-app-bar>
|
|
||||||
|
|
||||||
<v-card-text>
|
<v-tab>
|
||||||
<v-form ref="newUser">
|
Groups
|
||||||
<v-row>
|
<v-icon>mdi-account-group</v-icon>
|
||||||
<v-col cols="12" sm="12" md="6">
|
</v-tab>
|
||||||
<v-text-field
|
</v-tabs>
|
||||||
v-model="editedItem.fullName"
|
|
||||||
label="Full Name"
|
|
||||||
:rules="[existsRule]"
|
|
||||||
validate-on-blur
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="6">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.email"
|
|
||||||
label="Email"
|
|
||||||
:rules="[existsRule, emailRule]"
|
|
||||||
validate-on-blur
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="6">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.family"
|
|
||||||
label="Family Group"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.password"
|
|
||||||
label="User Password"
|
|
||||||
:rules="[existsRule, minRule]"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="3">
|
|
||||||
<v-switch
|
|
||||||
v-model="editedItem.admin"
|
|
||||||
label="Admin"
|
|
||||||
></v-switch>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-form>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
<v-tabs-items v-model="tab">
|
||||||
<v-spacer></v-spacer>
|
<v-tab-item>
|
||||||
<v-btn color="grey" text @click="close">
|
<TheUserTable />
|
||||||
Cancel
|
</v-tab-item>
|
||||||
</v-btn>
|
<v-tab-item>
|
||||||
<v-btn color="primary" @click="save">
|
<TheSignUpTable />
|
||||||
Save
|
</v-tab-item>
|
||||||
</v-btn>
|
<v-tab-item>
|
||||||
</v-card-actions>
|
<TheGroupTable />
|
||||||
</v-card>
|
</v-tab-item>
|
||||||
</v-dialog>
|
</v-tabs-items>
|
||||||
<Confirmation
|
</v-card>
|
||||||
ref="deleteUserDialog"
|
</div>
|
||||||
title="Confirm User Deletion"
|
|
||||||
:message="
|
|
||||||
`Are you sure you want to delete the user <b>${activeName} ID: ${activeId}<b/>`
|
|
||||||
"
|
|
||||||
icon="mdi-alert"
|
|
||||||
@confirm="deleteUser"
|
|
||||||
:width="450"
|
|
||||||
@close="closeDelete"
|
|
||||||
/>
|
|
||||||
</v-toolbar>
|
|
||||||
</template>
|
|
||||||
<template v-slot:item.actions="{ item }">
|
|
||||||
<v-btn class="mr-1" small color="error" @click="deleteItem(item)">
|
|
||||||
<v-icon small left>
|
|
||||||
mdi-delete
|
|
||||||
</v-icon>
|
|
||||||
Delete
|
|
||||||
</v-btn>
|
|
||||||
<v-btn small color="success" @click="editItem(item)">
|
|
||||||
<v-icon small left class="mr-2">
|
|
||||||
mdi-pencil
|
|
||||||
</v-icon>
|
|
||||||
Edit
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
<template v-slot:item.admin="{ item }">
|
|
||||||
{{ item.admin ? "Admin" : "User" }}
|
|
||||||
</template>
|
|
||||||
<template v-slot:no-data>
|
|
||||||
<v-btn color="primary" @click="initialize">
|
|
||||||
Reset
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</v-data-table>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Confirmation from "@/components/UI/Confirmation";
|
import TheUserTable from "@/components/Admin/ManageUsers/TheUserTable";
|
||||||
import api from "@/api";
|
import TheGroupTable from "@/components/Admin/ManageUsers/TheGroupTable";
|
||||||
import { validators } from "@/mixins/validators";
|
import TheSignUpTable from "@/components/Admin/ManageUsers/TheSignUpTable";
|
||||||
export default {
|
export default {
|
||||||
components: { Confirmation },
|
components: { TheUserTable, TheGroupTable, TheSignUpTable },
|
||||||
mixins: [validators],
|
data() {
|
||||||
data: () => ({
|
return {
|
||||||
dialog: false,
|
tab: 0,
|
||||||
activeId: null,
|
};
|
||||||
activeName: null,
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: "User ID",
|
|
||||||
align: "start",
|
|
||||||
sortable: false,
|
|
||||||
value: "id",
|
|
||||||
},
|
|
||||||
{ text: "Full Name", value: "fullName" },
|
|
||||||
{ text: "Email", value: "email" },
|
|
||||||
{ text: "Family", value: "family" },
|
|
||||||
{ text: "Admin", value: "admin" },
|
|
||||||
{ text: "", value: "actions", sortable: false, align: "center" },
|
|
||||||
],
|
|
||||||
users: [],
|
|
||||||
editedIndex: -1,
|
|
||||||
editedItem: {
|
|
||||||
id: 0,
|
|
||||||
fullName: "",
|
|
||||||
password: "",
|
|
||||||
email: "",
|
|
||||||
family: "",
|
|
||||||
admin: false,
|
|
||||||
},
|
|
||||||
defaultItem: {
|
|
||||||
id: 0,
|
|
||||||
fullName: "",
|
|
||||||
password: "",
|
|
||||||
email: "",
|
|
||||||
family: "",
|
|
||||||
admin: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
formTitle() {
|
|
||||||
return this.editedIndex === -1 ? "New User" : "Edit User";
|
|
||||||
},
|
|
||||||
showPassword() {
|
|
||||||
return this.editedIndex === -1 ? true : false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
dialog(val) {
|
|
||||||
val || this.close();
|
|
||||||
},
|
|
||||||
dialogDelete(val) {
|
|
||||||
val || this.closeDelete();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.initialize();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async initialize() {
|
|
||||||
this.users = await api.users.allUsers();
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteUser() {
|
|
||||||
await api.users.delete(this.activeId);
|
|
||||||
this.initialize();
|
|
||||||
},
|
|
||||||
|
|
||||||
editItem(item) {
|
|
||||||
this.editedIndex = this.users.indexOf(item);
|
|
||||||
this.editedItem = Object.assign({}, item);
|
|
||||||
this.dialog = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteItem(item) {
|
|
||||||
this.activeId = item.id;
|
|
||||||
this.activeName = item.fullName;
|
|
||||||
this.editedIndex = this.users.indexOf(item);
|
|
||||||
this.editedItem = Object.assign({}, item);
|
|
||||||
this.$refs.deleteUserDialog.open();
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteItemConfirm() {
|
|
||||||
this.users.splice(this.editedIndex, 1);
|
|
||||||
this.closeDelete();
|
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.dialog = false;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.editedItem = Object.assign({}, this.defaultItem);
|
|
||||||
this.editedIndex = -1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
closeDelete() {
|
|
||||||
this.dialogDelete = false;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.editedItem = Object.assign({}, this.defaultItem);
|
|
||||||
this.editedIndex = -1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
if (this.editedIndex > -1) {
|
|
||||||
api.users.update(this.editedItem);
|
|
||||||
this.close();
|
|
||||||
} else if (this.$refs.newUser.validate()) {
|
|
||||||
api.users.create(this.editedItem);
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
await this.initialize();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,6 +4,7 @@ from db.db_base import BaseDocument
|
||||||
from db.models.mealplan import MealPlanModel
|
from db.models.mealplan import MealPlanModel
|
||||||
from db.models.recipe import Category, RecipeModel, Tag
|
from db.models.recipe import Category, RecipeModel, Tag
|
||||||
from db.models.settings import SiteSettingsModel
|
from db.models.settings import SiteSettingsModel
|
||||||
|
from db.models.sign_up import SignUp
|
||||||
from db.models.theme import SiteThemeModel
|
from db.models.theme import SiteThemeModel
|
||||||
from db.models.users import User
|
from db.models.users import User
|
||||||
|
|
||||||
|
@ -70,6 +71,11 @@ class _Users(BaseDocument):
|
||||||
return return_data
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
class _SignUps(BaseDocument):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.primary_key = "token"
|
||||||
|
self.sql_model = SignUp
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
@ -80,6 +86,7 @@ class Database:
|
||||||
self.categories = _Categories()
|
self.categories = _Categories()
|
||||||
self.tags = _Tags()
|
self.tags = _Tags()
|
||||||
self.users = _Users()
|
self.users = _Users()
|
||||||
|
self.sign_ups = _SignUps()
|
||||||
|
|
||||||
|
|
||||||
db = Database()
|
db = Database()
|
||||||
|
|
|
@ -3,3 +3,4 @@ from db.models.recipe import *
|
||||||
from db.models.settings import *
|
from db.models.settings import *
|
||||||
from db.models.theme import *
|
from db.models.theme import *
|
||||||
from db.models.users import *
|
from db.models.users import *
|
||||||
|
from db.models.sign_up import *
|
||||||
|
|
25
mealie/db/models/sign_up.py
Normal file
25
mealie/db/models/sign_up.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
|
||||||
|
|
||||||
|
class SignUp(SqlAlchemyBase, BaseMixins):
|
||||||
|
__tablename__ = "sign_ups"
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
token = Column(String, nullable=False, index=True)
|
||||||
|
name = Column(String, index=True)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
session,
|
||||||
|
token,
|
||||||
|
name,
|
||||||
|
) -> None:
|
||||||
|
self.token = token
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def dict(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"name": self.name,
|
||||||
|
"token": self.token,
|
||||||
|
}
|
|
@ -20,4 +20,5 @@ def query_user(user_email: str, session: Session = None) -> UserInDB:
|
||||||
session = session if session else create_session()
|
session = session if session else create_session()
|
||||||
user = db.users.get(session, user_email, "email")
|
user = db.users.get(session, user_email, "email")
|
||||||
session.close()
|
session.close()
|
||||||
return UserInDB(**user)
|
return UserInDB(**user)
|
||||||
|
|
||||||
|
|
81
mealie/routes/users/sign_up.py
Normal file
81
mealie/routes/users/sign_up.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from core.security import get_password_hash
|
||||||
|
from db.database import db
|
||||||
|
from db.db_setup import generate_session
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from routes.deps import manager
|
||||||
|
from schema.sign_up import SignUpIn, SignUpOut, SignUpToken
|
||||||
|
from schema.snackbar import SnackResponse
|
||||||
|
from schema.user import UserIn, UserInDB
|
||||||
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=list[SignUpOut])
|
||||||
|
async def get_all_open_sign_ups(
|
||||||
|
current_user=Depends(manager),
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
):
|
||||||
|
""" Returns a list of open sign up links """
|
||||||
|
|
||||||
|
all_sign_ups = db.sign_ups.get_all(session)
|
||||||
|
|
||||||
|
return all_sign_ups
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", response_model=SignUpToken)
|
||||||
|
async def create_user_sign_up_key(
|
||||||
|
key_data: SignUpIn,
|
||||||
|
current_user: UserInDB = Depends(manager),
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
):
|
||||||
|
""" Generates a Random Token that a new user can sign up with """
|
||||||
|
|
||||||
|
if current_user.admin:
|
||||||
|
sign_up = {"token": str(uuid.uuid1().hex), "name": key_data.name}
|
||||||
|
db_entry = db.sign_ups.create(session, sign_up)
|
||||||
|
|
||||||
|
return db_entry
|
||||||
|
|
||||||
|
else:
|
||||||
|
return {"details": "not authorized"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{token}")
|
||||||
|
async def create_user_with_token(
|
||||||
|
token: str,
|
||||||
|
new_user: UserIn,
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
):
|
||||||
|
""" Creates a user with a valid sign up token """
|
||||||
|
|
||||||
|
# Validate Token
|
||||||
|
db_entry = db.sign_ups.get(session, token, limit=1)
|
||||||
|
if not db_entry:
|
||||||
|
return {"details": "invalid token"}
|
||||||
|
|
||||||
|
# Create User
|
||||||
|
new_user.password = get_password_hash(new_user.password)
|
||||||
|
data = db.users.create(session, new_user.dict())
|
||||||
|
|
||||||
|
# DeleteToken
|
||||||
|
db.sign_ups.delete(session, token)
|
||||||
|
|
||||||
|
# Respond
|
||||||
|
return SnackResponse.success(f"User Created: {new_user.full_name}", data)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{token}")
|
||||||
|
async def delete_token(
|
||||||
|
token: str,
|
||||||
|
current_user: UserInDB = Depends(manager),
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
):
|
||||||
|
""" Removed a token from the database """
|
||||||
|
if current_user.admin:
|
||||||
|
db.sign_ups.delete(session, token)
|
||||||
|
return SnackResponse.error("Sign Up Token Deleted")
|
||||||
|
else:
|
||||||
|
return {"details", "not authorized"}
|
|
@ -1,7 +1,8 @@
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from routes.users import auth, crud
|
from routes.users import auth, crud, sign_up
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
router.include_router(sign_up.router)
|
||||||
router.include_router(auth.router)
|
router.include_router(auth.router)
|
||||||
router.include_router(crud.router)
|
router.include_router(crud.router)
|
||||||
|
|
13
mealie/schema/sign_up.py
Normal file
13
mealie/schema/sign_up.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from fastapi_camelcase import CamelModel
|
||||||
|
|
||||||
|
|
||||||
|
class SignUpIn(CamelModel):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
class SignUpToken(SignUpIn):
|
||||||
|
token: str
|
||||||
|
|
||||||
|
|
||||||
|
class SignUpOut(SignUpToken):
|
||||||
|
id: int
|
Loading…
Add table
Add a link
Reference in a new issue