mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
password reset
This commit is contained in:
parent
5f5e2e47f7
commit
7024861397
14 changed files with 257 additions and 67 deletions
|
@ -11,6 +11,7 @@ const usersURLs = {
|
||||||
users: `${userPrefix}`,
|
users: `${userPrefix}`,
|
||||||
self: `${userPrefix}/self`,
|
self: `${userPrefix}/self`,
|
||||||
userID: id => `${userPrefix}/${id}`,
|
userID: id => `${userPrefix}/${id}`,
|
||||||
|
password: id => `${userPrefix}/${id}/password`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -42,6 +43,10 @@ export default {
|
||||||
let response = await apiReq.put(usersURLs.userID(user.id), user);
|
let response = await apiReq.put(usersURLs.userID(user.id), user);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
async changePassword(id, password) {
|
||||||
|
let response = await apiReq.put(usersURLs.password(id), password);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
async delete(id) {
|
async delete(id) {
|
||||||
let response = await apiReq.delete(usersURLs.userID(id));
|
let response = await apiReq.delete(usersURLs.userID(id));
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|
|
@ -27,8 +27,10 @@
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
|
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title>Jane Smith</v-list-item-title>
|
<v-list-item-title> {{ user.fullName }}</v-list-item-title>
|
||||||
<v-list-item-subtitle>Admin</v-list-item-subtitle>
|
<v-list-item-subtitle>
|
||||||
|
{{ user.admin ? "Admin" : "User" }}</v-list-item-subtitle
|
||||||
|
>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
|
@ -50,7 +52,7 @@
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-list nav dense>
|
<v-list nav dense v-if="user.admin">
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="nav in superLinks"
|
v-for="nav in superLinks"
|
||||||
:key="nav.title"
|
:key="nav.title"
|
||||||
|
@ -115,11 +117,17 @@ export default {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
async mounted() {
|
||||||
this.mobile = this.viewScale();
|
this.mobile = this.viewScale();
|
||||||
this.showSidebar = !this.viewScale();
|
this.showSidebar = !this.viewScale();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
user() {
|
||||||
|
return this.$store.getters.getUserData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
viewScale() {
|
viewScale() {
|
||||||
switch (this.$vuetify.breakpoint.name) {
|
switch (this.$vuetify.breakpoint.name) {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<v-card flat>
|
<v-card flat>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<h2 class="mt-1 mb-1">{{$t('settings.homepage.home-page')}}</h2>
|
<h2 class="mt-1 mb-1">{{ $t("settings.homepage.home-page") }}</h2>
|
||||||
<v-row align="center" justify="center" dense class="mb-n7 pb-n5">
|
<v-row align="center" justify="center" dense class="mb-n7 pb-n5">
|
||||||
<v-col cols="12" sm="3" md="2">
|
<v-col cols="12" sm="3" md="2">
|
||||||
<v-switch v-model="showRecent" :label="$t('settings.homepage.show-recent')"></v-switch>
|
<v-switch
|
||||||
|
v-model="showRecent"
|
||||||
|
:label="$t('settings.homepage.show-recent')"
|
||||||
|
></v-switch>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="5" md="5">
|
<v-col cols="12" sm="5" md="5">
|
||||||
<v-slider
|
<v-slider
|
||||||
|
|
|
@ -133,8 +133,11 @@ export default {
|
||||||
this.$emit("logged-in");
|
this.$emit("logged-in");
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
console.log(key);
|
this.$store.commit("setToken", key.data.access_token);
|
||||||
this.$store.commit("setToken", key.data.access_token)
|
|
||||||
|
let user = await api.users.self();
|
||||||
|
this.$store.commit("setUserData", user);
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -186,7 +186,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteUser() {
|
async deleteUser() {
|
||||||
await api.users.delete(this.editedIndex);
|
await api.users.delete(this.activeId);
|
||||||
this.initialize();
|
this.initialize();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -227,7 +227,6 @@ export default {
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (this.editedIndex > -1) {
|
if (this.editedIndex > -1) {
|
||||||
console.log("New User", this.editedItem);
|
|
||||||
api.users.update(this.editedItem);
|
api.users.update(this.editedItem);
|
||||||
} else {
|
} else {
|
||||||
api.users.create(this.editedItem);
|
api.users.create(this.editedItem);
|
||||||
|
|
|
@ -1,50 +1,98 @@
|
||||||
<template>
|
<template>
|
||||||
<v-card>
|
<v-row dense>
|
||||||
<v-card-title class="headline">
|
<v-col cols="12" md="12" sm="12">
|
||||||
<span>
|
<v-card>
|
||||||
<v-avatar color="accent" size="40" class="mr-2" v-if="!loading">
|
<v-card-title class="headline">
|
||||||
<img src="https://cdn.vuetifyjs.com/images/john.jpg" alt="John" />
|
<span>
|
||||||
</v-avatar>
|
<v-avatar color="accent" size="40" class="mr-2" v-if="!loading">
|
||||||
<v-progress-circular
|
<img src="https://cdn.vuetifyjs.com/images/john.jpg" alt="John" />
|
||||||
v-else
|
</v-avatar>
|
||||||
indeterminate
|
<v-progress-circular
|
||||||
color="primary"
|
v-else
|
||||||
large
|
indeterminate
|
||||||
class="mr-2"
|
color="primary"
|
||||||
>
|
large
|
||||||
</v-progress-circular>
|
class="mr-2"
|
||||||
</span>
|
>
|
||||||
Profile
|
</v-progress-circular>
|
||||||
<v-spacer></v-spacer>
|
</span>
|
||||||
User ID: {{ user.id }}
|
Profile
|
||||||
</v-card-title>
|
<v-spacer></v-spacer>
|
||||||
<v-divider></v-divider>
|
User ID: {{ user.id }}
|
||||||
<v-card-text>
|
</v-card-title>
|
||||||
<v-form>
|
<v-divider></v-divider>
|
||||||
<v-text-field label="Full Name" v-model="user.fullName"> </v-text-field>
|
<v-card-text>
|
||||||
<v-text-field label="Email" v-model="user.email"> </v-text-field>
|
<v-form>
|
||||||
<v-text-field
|
<v-text-field label="Full Name" v-model="user.fullName">
|
||||||
label="Family"
|
</v-text-field>
|
||||||
readonly
|
<v-text-field label="Email" v-model="user.email"> </v-text-field>
|
||||||
v-model="user.family"
|
<v-text-field
|
||||||
persistent-hint
|
label="Family"
|
||||||
hint="Family groups can only be set by administrators"
|
readonly
|
||||||
>
|
v-model="user.family"
|
||||||
</v-text-field>
|
persistent-hint
|
||||||
</v-form>
|
hint="Family groups can only be set by administrators"
|
||||||
</v-card-text>
|
>
|
||||||
<v-card-actions>
|
</v-text-field>
|
||||||
<v-btn color="accent" class="mr-2">
|
</v-form>
|
||||||
<v-icon left> mdi-lock </v-icon>
|
</v-card-text>
|
||||||
{{ $t("settings.change-password") }}
|
<v-card-actions>
|
||||||
</v-btn>
|
<v-spacer></v-spacer>
|
||||||
<v-spacer></v-spacer>
|
<v-btn color="success" class="mr-2" @click="updateUser">
|
||||||
<v-btn color="success" class="mr-2" @click="updateUser">
|
<v-icon left> mdi-content-save </v-icon>
|
||||||
<v-icon left> mdi-content-save </v-icon>
|
{{ $t("general.save") }}
|
||||||
{{ $t("general.save") }}
|
</v-btn>
|
||||||
</v-btn>
|
</v-card-actions>
|
||||||
</v-card-actions>
|
</v-card>
|
||||||
</v-card>
|
</v-col>
|
||||||
|
<v-col cols="12" md="4" sm="12">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="headline">
|
||||||
|
Reset Password
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-card-title>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form>
|
||||||
|
<v-text-field
|
||||||
|
v-model="password.current"
|
||||||
|
light="light"
|
||||||
|
prepend-icon="mdi-lock"
|
||||||
|
label="Current Password"
|
||||||
|
:type="showPassword.current ? 'text' : 'password'"
|
||||||
|
:append-icon="showPassword.current ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
|
@click:append="showPassword.current = !showPassword.current"
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="password.newOne"
|
||||||
|
light="light"
|
||||||
|
prepend-icon="mdi-lock"
|
||||||
|
label="New Password"
|
||||||
|
:type="showPassword.newOne ? 'text' : 'password'"
|
||||||
|
:append-icon="showPassword.newOne ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
|
@click:append="showPassword.newOne = !showPassword.newOne"
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="password.newTwo"
|
||||||
|
light="light"
|
||||||
|
prepend-icon="mdi-lock"
|
||||||
|
label="Confirm Password"
|
||||||
|
:type="showPassword.newTwo ? 'text' : 'password'"
|
||||||
|
:append-icon="showPassword.newTwo ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
|
@click:append="showPassword.newTwo = !showPassword.newTwo"
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="accent" class="mr-2" @click="changePassword">
|
||||||
|
<v-icon left> mdi-lock </v-icon>
|
||||||
|
{{ $t("settings.change-password") }}
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -54,6 +102,16 @@ export default {
|
||||||
pageTitle: "My Profile",
|
pageTitle: "My Profile",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
password: {
|
||||||
|
current: "",
|
||||||
|
newOne: "",
|
||||||
|
newTwo: "",
|
||||||
|
},
|
||||||
|
showPassword: {
|
||||||
|
current: false,
|
||||||
|
newOne: false,
|
||||||
|
newTwo: false,
|
||||||
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
user: {
|
user: {
|
||||||
fullName: "Change Me",
|
fullName: "Change Me",
|
||||||
|
@ -87,6 +145,14 @@ export default {
|
||||||
this.refreshProfile();
|
this.refreshProfile();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
|
async changePassword() {
|
||||||
|
let data = {
|
||||||
|
currentPassword: this.password.current,
|
||||||
|
newPassword: this.password.newOne,
|
||||||
|
};
|
||||||
|
|
||||||
|
await api.users.changePassword(this.user.id, data);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
|
@ -4,15 +4,70 @@
|
||||||
<v-slide-x-transition hide-on-leave>
|
<v-slide-x-transition hide-on-leave>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</v-slide-x-transition>
|
</v-slide-x-transition>
|
||||||
|
<!-- <v-footer fixed>
|
||||||
|
<v-col class="text-center" cols="12">
|
||||||
|
{{ $t("settings.current") }}
|
||||||
|
{{ version }} |
|
||||||
|
{{ $t("settings.latest") }}
|
||||||
|
{{ latestVersion }}
|
||||||
|
·
|
||||||
|
<a href="https://hay-kot.github.io/mealie/" target="_blank">
|
||||||
|
{{ $t("settings.explore-the-docs") }}
|
||||||
|
</a>
|
||||||
|
·
|
||||||
|
<a
|
||||||
|
href="https://hay-kot.github.io/mealie/contributors/non-coders/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ $t("settings.contribute") }}
|
||||||
|
</a>
|
||||||
|
</v-col>
|
||||||
|
</v-footer> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AdminSidebar from "../../components/Admin/AdminSidebar";
|
import AdminSidebar from "@/components/Admin/AdminSidebar";
|
||||||
|
import axios from "axios";
|
||||||
|
import api from "@/api";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AdminSidebar,
|
AdminSidebar,
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
latestVersion: null,
|
||||||
|
version: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
this.getVersion();
|
||||||
|
let versionData = await api.meta.get_version();
|
||||||
|
this.version = versionData.version;
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
newVersion() {
|
||||||
|
if ((this.latestVersion != null) & (this.latestVersion != this.version)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getVersion() {
|
||||||
|
let response = await axios.get(
|
||||||
|
"https://api.github.com/repos/hay-kot/mealie/releases/latest",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
Authorization: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.latestVersion = response.data.tag_name;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ const state = {
|
||||||
isDark: false,
|
isDark: false,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
token: "",
|
token: "",
|
||||||
|
userData: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
|
@ -46,6 +47,10 @@ const mutations = {
|
||||||
axios.defaults.headers.common["Authorization"] = `Bearer ${payload}`;
|
axios.defaults.headers.common["Authorization"] = `Bearer ${payload}`;
|
||||||
state.token = payload;
|
state.token = payload;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setUserData(state, payload) {
|
||||||
|
state.userData = payload;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
|
@ -58,7 +63,6 @@ const actions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
async initTheme({ dispatch, getters }) {
|
async initTheme({ dispatch, getters }) {
|
||||||
//If theme is empty resetTheme
|
//If theme is empty resetTheme
|
||||||
if (Object.keys(getters.getActiveTheme).length === 0) {
|
if (Object.keys(getters.getActiveTheme).length === 0) {
|
||||||
|
@ -77,6 +81,7 @@ const getters = {
|
||||||
getIsDark: state => state.isDark,
|
getIsDark: state => state.isDark,
|
||||||
getIsLoggedIn: state => state.isLoggedIn,
|
getIsLoggedIn: state => state.isLoggedIn,
|
||||||
getToken: state => state.token,
|
getToken: state => state.token,
|
||||||
|
getUserData: state => state.userData,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -46,6 +46,7 @@ NEXTCLOUD_DIR = MIGRATION_DIR.joinpath("nextcloud")
|
||||||
CHOWDOWN_DIR = MIGRATION_DIR.joinpath("chowdown")
|
CHOWDOWN_DIR = MIGRATION_DIR.joinpath("chowdown")
|
||||||
TEMPLATE_DIR = DATA_DIR.joinpath("templates")
|
TEMPLATE_DIR = DATA_DIR.joinpath("templates")
|
||||||
SQLITE_DIR = DATA_DIR.joinpath("db")
|
SQLITE_DIR = DATA_DIR.joinpath("db")
|
||||||
|
RECIPE_DATA_DIR = DATA_DIR.joinpath("recipes")
|
||||||
TEMP_DIR = DATA_DIR.joinpath(".temp")
|
TEMP_DIR = DATA_DIR.joinpath(".temp")
|
||||||
|
|
||||||
REQUIRED_DIRS = [
|
REQUIRED_DIRS = [
|
||||||
|
@ -58,6 +59,7 @@ REQUIRED_DIRS = [
|
||||||
SQLITE_DIR,
|
SQLITE_DIR,
|
||||||
NEXTCLOUD_DIR,
|
NEXTCLOUD_DIR,
|
||||||
CHOWDOWN_DIR,
|
CHOWDOWN_DIR,
|
||||||
|
RECIPE_DATA_DIR
|
||||||
]
|
]
|
||||||
|
|
||||||
ensure_dirs()
|
ensure_dirs()
|
||||||
|
|
|
@ -61,6 +61,15 @@ class _Users(BaseDocument):
|
||||||
self.primary_key = "id"
|
self.primary_key = "id"
|
||||||
self.sql_model = User
|
self.sql_model = User
|
||||||
|
|
||||||
|
def update_password(self, session, id, password: str):
|
||||||
|
entry = self._query_one(session=session, match_value=id)
|
||||||
|
entry.update_password(password)
|
||||||
|
return_data = entry.dict()
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
|
@ -42,3 +42,6 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||||
self.email = email
|
self.email = email
|
||||||
self.family = family
|
self.family = family
|
||||||
self.admin = admin
|
self.admin = admin
|
||||||
|
|
||||||
|
def update_password(self, password):
|
||||||
|
self.password = password
|
||||||
|
|
|
@ -8,6 +8,7 @@ from fastapi_login.exceptions import InvalidCredentialsException
|
||||||
from routes.deps import manager, query_user
|
from routes.deps import manager, query_user
|
||||||
from schema.user import UserInDB
|
from schema.user import UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
from schema.snackbar import SnackResponse
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/auth", tags=["Auth"])
|
router = APIRouter(prefix="/api/auth", tags=["Auth"])
|
||||||
|
|
||||||
|
@ -29,4 +30,4 @@ def token(
|
||||||
access_token = manager.create_access_token(
|
access_token = manager.create_access_token(
|
||||||
data=dict(sub=email), expires=timedelta(hours=2)
|
data=dict(sub=email), expires=timedelta(hours=2)
|
||||||
)
|
)
|
||||||
return {"access_token": access_token, "token_type": "bearer"}
|
return SnackResponse.success("User Successfully Logged In", {"access_token": access_token, "token_type": "bearer"})
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from core.security import get_password_hash
|
from core.security import get_password_hash, verify_password
|
||||||
from db.database import db
|
from db.database import db
|
||||||
from db.db_setup import generate_session
|
from db.db_setup import generate_session
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from routes.deps import manager, query_user
|
from routes.deps import manager, query_user
|
||||||
from schema.user import UserBase, UserIn, UserInDB, UserOut
|
from schema.snackbar import SnackResponse
|
||||||
|
from schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/users", tags=["Users"])
|
router = APIRouter(prefix="/api/users", tags=["Users"])
|
||||||
|
@ -17,12 +18,11 @@ async def create_user(
|
||||||
current_user=Depends(manager),
|
current_user=Depends(manager),
|
||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
):
|
):
|
||||||
""" Returns a list of all user in the Database """
|
|
||||||
|
|
||||||
new_user.password = get_password_hash(new_user.password)
|
new_user.password = get_password_hash(new_user.password)
|
||||||
|
|
||||||
data = db.users.create(session, new_user.dict())
|
data = db.users.create(session, new_user.dict())
|
||||||
return data
|
return SnackResponse.success(f"User Created: {new_user.full_name}", data)
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[UserOut])
|
@router.get("", response_model=list[UserOut])
|
||||||
|
@ -38,7 +38,7 @@ async def get_all_users(
|
||||||
|
|
||||||
|
|
||||||
@router.get("/self", response_model=UserOut)
|
@router.get("/self", response_model=UserOut)
|
||||||
async def get_user_by_id(
|
async def get_logged_in_user(
|
||||||
current_user: UserInDB = Depends(manager),
|
current_user: UserInDB = Depends(manager),
|
||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
):
|
):
|
||||||
|
@ -69,8 +69,30 @@ async def update_user(
|
||||||
access_token = manager.create_access_token(
|
access_token = manager.create_access_token(
|
||||||
data=dict(sub=email), expires=timedelta(hours=2)
|
data=dict(sub=email), expires=timedelta(hours=2)
|
||||||
)
|
)
|
||||||
return {"access_token": access_token, "token_type": "bearer"}
|
access_token = {"access_token": access_token, "token_type": "bearer"}
|
||||||
return
|
|
||||||
|
return SnackResponse.success("User Updated", access_token)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{id}/password")
|
||||||
|
async def update_password(
|
||||||
|
id: int,
|
||||||
|
password_change: ChangePassword,
|
||||||
|
current_user: UserInDB = Depends(manager),
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
):
|
||||||
|
""" Resets the User Password"""
|
||||||
|
|
||||||
|
match_passwords = verify_password(
|
||||||
|
password_change.current_password, current_user.password
|
||||||
|
)
|
||||||
|
match_id = current_user.id == id
|
||||||
|
|
||||||
|
if match_passwords and match_id:
|
||||||
|
new_password = get_password_hash(password_change.new_password)
|
||||||
|
db.users.update_password(session, id, new_password)
|
||||||
|
|
||||||
|
return SnackResponse.success("Password Updated")
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{id}")
|
@router.delete("/{id}")
|
||||||
|
@ -81,5 +103,9 @@ async def delete_user(
|
||||||
):
|
):
|
||||||
""" Removes a user from the database. Must be the current user or a super user"""
|
""" Removes a user from the database. Must be the current user or a super user"""
|
||||||
|
|
||||||
|
if id == 1:
|
||||||
|
return SnackResponse.error("Error! Cannot Delete Super User")
|
||||||
|
|
||||||
if current_user.id == id or current_user.admin:
|
if current_user.id == id or current_user.admin:
|
||||||
return db.users.delete(session, id)
|
db.users.delete(session, id)
|
||||||
|
return SnackResponse.error(f"User Deleted")
|
||||||
|
|
|
@ -5,6 +5,11 @@ from fastapi_camelcase import CamelModel
|
||||||
# from pydantic import EmailStr
|
# from pydantic import EmailStr
|
||||||
|
|
||||||
|
|
||||||
|
class ChangePassword(CamelModel):
|
||||||
|
current_password: str
|
||||||
|
new_password: str
|
||||||
|
|
||||||
|
|
||||||
class UserBase(CamelModel):
|
class UserBase(CamelModel):
|
||||||
full_name: Optional[str] = None
|
full_name: Optional[str] = None
|
||||||
email: str
|
email: str
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue