mirror of
https://github.com/usatiuk/writer.git
synced 2025-10-28 16:07:49 +01:00
basic password change
This commit is contained in:
4668
frontend/package-lock.json
generated
4668
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,38 +6,38 @@
|
|||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/autoprefixer": "^9.7.1",
|
"@types/autoprefixer": "^9.7.2",
|
||||||
"@types/enzyme": "^3.10.5",
|
"@types/enzyme": "^3.10.5",
|
||||||
"@types/enzyme-adapter-react-16": "^1.0.6",
|
"@types/enzyme-adapter-react-16": "^1.0.6",
|
||||||
"@types/highlight.js": "^9.12.3",
|
"@types/highlight.js": "^9.12.4",
|
||||||
"@types/jest": "^25.1.4",
|
"@types/jest": "^25.2.3",
|
||||||
"@types/parcel-bundler": "^1.12.1",
|
"@types/parcel-bundler": "^1.12.1",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.35",
|
||||||
"@types/react-dom": "^16.9.5",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-redux": "^7.1.7",
|
"@types/react-redux": "^7.1.9",
|
||||||
"@types/react-router": "^5.1.4",
|
"@types/react-router": "^5.1.7",
|
||||||
"@types/react-router-dom": "^5.1.3",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
"@types/sass": "^1.16.0",
|
"@types/sass": "^1.16.0",
|
||||||
"autoprefixer": "^9.7.4",
|
"autoprefixer": "^9.8.0",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-adapter-react-16": "^1.15.2",
|
"enzyme-adapter-react-16": "^1.15.2",
|
||||||
"jest": "^25.1.0",
|
"jest": "^26.0.1",
|
||||||
"parcel-bundler": "^1.12.4",
|
"parcel-bundler": "^1.12.4",
|
||||||
"postcss-modules": "^1.5.0",
|
"postcss-modules": "^2.0.0",
|
||||||
"redux-devtools-extension": "^2.13.8",
|
"redux-devtools-extension": "^2.13.8",
|
||||||
"sass": "^1.26.3",
|
"sass": "^1.26.8",
|
||||||
"ts-jest": "^25.2.1"
|
"ts-jest": "^26.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blueprintjs/core": "^3.24.0",
|
"@blueprintjs/core": "^3.28.1",
|
||||||
"@blueprintjs/icons": "^3.14.0",
|
"@blueprintjs/icons": "^3.18.0",
|
||||||
"highlight.js": "^9.18.1",
|
"highlight.js": "^10.0.3",
|
||||||
"react": "^16.13.0",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.0",
|
"react-dom": "^16.13.1",
|
||||||
"react-markdown": "^4.3.1",
|
"react-markdown": "^4.3.1",
|
||||||
"react-redux": "^7.2.0",
|
"react-redux": "^7.2.0",
|
||||||
"react-router": "^5.1.2",
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
|
|||||||
@@ -1,7 +1,76 @@
|
|||||||
import * as React from "react";
|
import { Button, Card, FormGroup, H2, InputGroup } from "@blueprintjs/core";
|
||||||
|
import * as React from "react";
|
||||||
export function AccountComponent() {
|
import { connect } from "react-redux";
|
||||||
return <div>Hello</div>;
|
import { Dispatch } from "redux";
|
||||||
}
|
import { IAppState } from "~redux/reducers";
|
||||||
|
import { userPassChange } from "~redux/user/actions";
|
||||||
export { AccountComponent as Account };
|
|
||||||
|
export interface IAccountComponentProps {
|
||||||
|
username: string;
|
||||||
|
changePass: (password: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccountComponent(props: IAccountComponentProps) {
|
||||||
|
const [pass, setPass] = React.useState("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="AuthForm" elevation={2}>
|
||||||
|
<form
|
||||||
|
onSubmit={(e: React.FormEvent<any>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (pass.trim()) {
|
||||||
|
props.changePass(pass);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="header">
|
||||||
|
<H2>Account</H2>
|
||||||
|
</div>
|
||||||
|
<FormGroup label="Username">
|
||||||
|
<InputGroup
|
||||||
|
name="username"
|
||||||
|
leftIcon="person"
|
||||||
|
disabled={true}
|
||||||
|
value={props.username}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup label="Password">
|
||||||
|
<InputGroup
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
leftIcon="key"
|
||||||
|
value={pass}
|
||||||
|
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||||
|
setPass(e.currentTarget.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<div className="footer">
|
||||||
|
<Button
|
||||||
|
className="submit"
|
||||||
|
intent="primary"
|
||||||
|
icon="floppy-disk"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: IAppState) {
|
||||||
|
return { username: state.user.user.username };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: Dispatch) {
|
||||||
|
return {
|
||||||
|
changePass: (password: string) => dispatch(userPassChange(password)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Account = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(AccountComponent);
|
||||||
|
|||||||
@@ -24,3 +24,19 @@ export function showSharedToast() {
|
|||||||
timeout: 2000,
|
timeout: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function showPasswordSavedToast() {
|
||||||
|
AppToaster.show({
|
||||||
|
message: "Password saved!",
|
||||||
|
intent: "success",
|
||||||
|
timeout: 2000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showPasswordNotSavedToast(error: string) {
|
||||||
|
AppToaster.show({
|
||||||
|
message: "Password not saved! " + error,
|
||||||
|
intent: "danger",
|
||||||
|
timeout: 2000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,3 +3,7 @@ import { fetchJSON, fetchJSONAuth } from "../utils";
|
|||||||
export async function fetchUser() {
|
export async function fetchUser() {
|
||||||
return fetchJSONAuth("/users/user", "GET");
|
return fetchJSONAuth("/users/user", "GET");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function changeUserPassword(newPassword: string) {
|
||||||
|
return fetchJSONAuth("/users/edit", "POST", { password: newPassword });
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { Action } from "redux";
|
import { Action } from "redux";
|
||||||
import { IUserAuthJSON, IUserJSON } from "~../../src/entity/User";
|
import { IUserAuthJSON, IUserJSON } from "~../../src/entity/User";
|
||||||
|
import { showPasswordNotSavedToast, showPasswordSavedToast } from "~AppToaster";
|
||||||
|
|
||||||
export enum UserTypes {
|
export enum UserTypes {
|
||||||
USER_GET = "USER_GET",
|
USER_GET = "USER_GET",
|
||||||
USER_GET_SUCCESS = "USER_GET_SUCCESS",
|
USER_GET_SUCCESS = "USER_GET_SUCCESS",
|
||||||
USER_GET_FAIL = "USER_GET_FAIL",
|
USER_GET_FAIL = "USER_GET_FAIL",
|
||||||
USER_LOGOUT = "USER_LOGOUT",
|
USER_LOGOUT = "USER_LOGOUT",
|
||||||
|
USER_PASS_CHANGE = "USER_PASS_CHANGE",
|
||||||
|
USER_PASS_CHANGE_SUCCESS = "USER_PASS_CHANGE_SUCCESS",
|
||||||
|
USER_PASS_CHANGE_FAIL = "USER_PASS_CHANGE_FAIL",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserGetAction extends Action {
|
export interface IUserGetAction extends Action {
|
||||||
@@ -29,6 +33,24 @@ export interface IUserGetFailAction extends Action {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IUserPassChangeAction extends Action {
|
||||||
|
type: UserTypes.USER_PASS_CHANGE;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserPassChangeSuccessAction extends Action {
|
||||||
|
type: UserTypes.USER_PASS_CHANGE_SUCCESS;
|
||||||
|
payload: IUserAuthJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserPassChangeFailAction extends Action {
|
||||||
|
type: UserTypes.USER_PASS_CHANGE_FAIL;
|
||||||
|
payload: {
|
||||||
|
error: string;
|
||||||
|
logout: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function getUser(): IUserGetAction {
|
export function getUser(): IUserGetAction {
|
||||||
return { type: UserTypes.USER_GET };
|
return { type: UserTypes.USER_GET };
|
||||||
}
|
}
|
||||||
@@ -48,8 +70,33 @@ export function getUserFail(
|
|||||||
return { type: UserTypes.USER_GET_FAIL, payload: { error, logout } };
|
return { type: UserTypes.USER_GET_FAIL, payload: { error, logout } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function userPassChange(password: string): IUserPassChangeAction {
|
||||||
|
return { type: UserTypes.USER_PASS_CHANGE, password };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function userPassChangeSuccess(
|
||||||
|
user: IUserAuthJSON,
|
||||||
|
): IUserPassChangeSuccessAction {
|
||||||
|
showPasswordSavedToast();
|
||||||
|
return { type: UserTypes.USER_PASS_CHANGE_SUCCESS, payload: user };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function userPassChangeFail(
|
||||||
|
error: string,
|
||||||
|
logout: boolean,
|
||||||
|
): IUserPassChangeFailAction {
|
||||||
|
showPasswordNotSavedToast(error);
|
||||||
|
return {
|
||||||
|
type: UserTypes.USER_PASS_CHANGE_FAIL,
|
||||||
|
payload: { error, logout },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type UserAction =
|
export type UserAction =
|
||||||
| IUserGetAction
|
| IUserGetAction
|
||||||
| IUserGetSuccessAction
|
| IUserGetSuccessAction
|
||||||
| IUserGetFailAction
|
| IUserGetFailAction
|
||||||
| IUserLogoutAction;
|
| IUserLogoutAction
|
||||||
|
| IUserPassChangeAction
|
||||||
|
| IUserPassChangeFailAction
|
||||||
|
| IUserPassChangeSuccessAction;
|
||||||
|
|||||||
@@ -18,12 +18,14 @@ export const userReducer: Reducer<IUserState, AuthAction> = (
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case AuthTypes.AUTH_SUCCESS:
|
case AuthTypes.AUTH_SUCCESS:
|
||||||
case UserTypes.USER_GET_SUCCESS:
|
case UserTypes.USER_GET_SUCCESS:
|
||||||
|
case UserTypes.USER_PASS_CHANGE_SUCCESS:
|
||||||
return {
|
return {
|
||||||
...defaultUserState,
|
...defaultUserState,
|
||||||
user: action.payload,
|
user: action.payload,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case UserTypes.USER_GET_FAIL:
|
case UserTypes.USER_GET_FAIL:
|
||||||
|
case UserTypes.USER_PASS_CHANGE_FAIL:
|
||||||
return defaultUserState;
|
return defaultUserState;
|
||||||
break;
|
break;
|
||||||
case UserTypes.USER_LOGOUT:
|
case UserTypes.USER_LOGOUT:
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import { all, call, delay, put, race, takeLatest } from "redux-saga/effects";
|
import { all, call, delay, put, race, takeLatest } from "redux-saga/effects";
|
||||||
import { fetchUser } from "~redux/api/user";
|
import { changeUserPassword, fetchUser } from "~redux/api/user";
|
||||||
|
|
||||||
import { getUserFail, getUserSuccess, UserTypes } from "./actions";
|
import {
|
||||||
|
getUserFail,
|
||||||
|
getUserSuccess,
|
||||||
|
IUserPassChangeAction,
|
||||||
|
userPassChangeFail,
|
||||||
|
userPassChangeSuccess,
|
||||||
|
UserTypes,
|
||||||
|
} from "./actions";
|
||||||
|
|
||||||
function* getUser() {
|
function* getUser() {
|
||||||
try {
|
try {
|
||||||
@@ -25,6 +32,31 @@ function* getUser() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* userSaga() {
|
function* userPassChange(action: IUserPassChangeAction) {
|
||||||
yield all([takeLatest(UserTypes.USER_GET, getUser)]);
|
try {
|
||||||
|
const { response, timeout } = yield race({
|
||||||
|
response: call(changeUserPassword, action.password),
|
||||||
|
timeout: delay(10000),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
yield put(userPassChangeFail("Timeout", false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response.data) {
|
||||||
|
const user = response.data;
|
||||||
|
yield put(userPassChangeSuccess(user));
|
||||||
|
} else {
|
||||||
|
yield put(userPassChangeFail(response.error, true));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
yield put(userPassChangeFail("Internal error", false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* userSaga() {
|
||||||
|
yield all([
|
||||||
|
takeLatest(UserTypes.USER_GET, getUser),
|
||||||
|
takeLatest(UserTypes.USER_PASS_CHANGE, userPassChange),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
1457
package-lock.json
generated
1457
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
66
package.json
66
package.json
@@ -1,66 +1,66 @@
|
|||||||
{
|
{
|
||||||
"name": "writer-backend",
|
"name": "writer-backend",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blueprintjs/tslint-config": "^2.0.0",
|
"@blueprintjs/tslint-config": "^3.0.0",
|
||||||
"@types/bcrypt": "^3.0.0",
|
"@types/bcrypt": "^3.0.0",
|
||||||
"@types/chai": "^4.2.11",
|
"@types/chai": "^4.2.11",
|
||||||
"@types/concurrently": "^5.1.0",
|
"@types/concurrently": "^5.2.1",
|
||||||
"@types/eslint": "^6.1.8",
|
"@types/eslint": "^6.8.1",
|
||||||
"@types/eslint-plugin-prettier": "^2.2.0",
|
"@types/eslint-plugin-prettier": "^3.1.0",
|
||||||
"@types/jsonwebtoken": "^8.3.8",
|
"@types/jsonwebtoken": "^8.5.0",
|
||||||
"@types/koa": "^2.11.2",
|
"@types/koa": "^2.11.3",
|
||||||
"@types/koa-logger": "^3.1.1",
|
"@types/koa-logger": "^3.1.1",
|
||||||
"@types/koa-router": "^7.4.0",
|
"@types/koa-router": "^7.4.1",
|
||||||
"@types/koa-send": "^4.1.2",
|
"@types/koa-send": "^4.1.2",
|
||||||
"@types/koa-sslify": "^4.0.1",
|
"@types/koa-sslify": "^4.0.1",
|
||||||
"@types/koa-static": "^4.0.1",
|
"@types/koa-static": "^4.0.1",
|
||||||
"@types/koa__cors": "^3.0.1",
|
"@types/koa__cors": "^3.0.1",
|
||||||
"@types/lodash": "^4.14.149",
|
"@types/lodash": "^4.14.155",
|
||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/mysql": "^2.15.9",
|
"@types/mysql": "^2.15.13",
|
||||||
"@types/node": "^13.9.1",
|
"@types/node": "^14.0.12",
|
||||||
"@types/prettier": "^1.19.0",
|
"@types/prettier": "^2.0.1",
|
||||||
"@types/supertest": "^2.0.8",
|
"@types/supertest": "^2.0.9",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^7.2.0",
|
||||||
"eslint-config-airbnb": "^18.1.0",
|
"eslint-config-airbnb": "^18.1.0",
|
||||||
"eslint-config-prettier": "^6.10.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-import": "^2.20.1",
|
"eslint-plugin-import": "^2.21.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||||
"eslint-plugin-prettier": "^3.1.2",
|
"eslint-plugin-prettier": "^3.1.3",
|
||||||
"eslint-plugin-react": "^7.19.0",
|
"eslint-plugin-react": "^7.20.0",
|
||||||
"eslint-plugin-react-hooks": "^2.5.0",
|
"eslint-plugin-react-hooks": "^4.0.4",
|
||||||
"husky": "^4.2.3",
|
"husky": "^4.2.5",
|
||||||
"mocha": "^7.1.0",
|
"mocha": "^7.2.0",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^2.0.5",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^4.0.2",
|
||||||
"ts-node-dev": "^1.0.0-pre.44",
|
"ts-node-dev": "^1.0.0-pre.44",
|
||||||
"tslint": "^6.1.0",
|
"tslint": "^6.1.2",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"tslint-no-unused-expression-chai": "^0.1.4",
|
"tslint-no-unused-expression-chai": "^0.1.4",
|
||||||
"tslint-plugin-prettier": "^2.2.0"
|
"tslint-plugin-prettier": "^2.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/cors": "^3.0.0",
|
"@koa/cors": "^3.1.0",
|
||||||
"bcrypt": "^4.0.1",
|
"bcrypt": "^5.0.0",
|
||||||
"concurrently": "^5.1.0",
|
"concurrently": "^5.2.0",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"koa": "^2.11.0",
|
"koa": "^2.12.0",
|
||||||
"koa-body": "^4.1.1",
|
"koa-body": "^4.1.3",
|
||||||
"koa-jwt": "^3.6.0",
|
"koa-jwt": "^4.0.0",
|
||||||
"koa-logger": "^3.2.1",
|
"koa-logger": "^3.2.1",
|
||||||
"koa-router": "^8.0.8",
|
"koa-router": "^9.0.1",
|
||||||
"koa-send": "^5.0.0",
|
"koa-send": "^5.0.0",
|
||||||
"koa-sslify": "^4.0.3",
|
"koa-sslify": "^4.0.3",
|
||||||
"koa-static": "^5.0.0",
|
"koa-static": "^5.0.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"ts-node": "8.6.2",
|
"ts-node": "8.10.2",
|
||||||
"tsconfig-paths": "^3.9.0",
|
"tsconfig-paths": "^3.9.0",
|
||||||
"typeorm": "0.2.24",
|
"typeorm": "0.2.25",
|
||||||
"typescript": "3.8.3"
|
"typescript": "3.9.5"
|
||||||
},
|
},
|
||||||
"cacheDirectories": [
|
"cacheDirectories": [
|
||||||
"frontend/node_modules",
|
"frontend/node_modules",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { IUserJWT, User } from "~entity/User";
|
|||||||
|
|
||||||
export const userRouter = new Router();
|
export const userRouter = new Router();
|
||||||
|
|
||||||
userRouter.get("/users/user", async ctx => {
|
userRouter.get("/users/user", async (ctx) => {
|
||||||
if (!ctx.state.user) {
|
if (!ctx.state.user) {
|
||||||
ctx.throw(401);
|
ctx.throw(401);
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ userRouter.get("/users/user", async ctx => {
|
|||||||
ctx.body = { error: false, data: user.toAuthJSON() };
|
ctx.body = { error: false, data: user.toAuthJSON() };
|
||||||
});
|
});
|
||||||
|
|
||||||
userRouter.post("/users/login", async ctx => {
|
userRouter.post("/users/login", async (ctx) => {
|
||||||
const request = ctx.request as any;
|
const request = ctx.request as any;
|
||||||
|
|
||||||
if (!request.body) {
|
if (!request.body) {
|
||||||
@@ -38,7 +38,7 @@ userRouter.post("/users/login", async ctx => {
|
|||||||
ctx.body = { error: false, data: user.toAuthJSON() };
|
ctx.body = { error: false, data: user.toAuthJSON() };
|
||||||
});
|
});
|
||||||
|
|
||||||
userRouter.post("/users/signup", async ctx => {
|
userRouter.post("/users/signup", async (ctx) => {
|
||||||
const request = ctx.request as any;
|
const request = ctx.request as any;
|
||||||
|
|
||||||
if (!request.body) {
|
if (!request.body) {
|
||||||
@@ -68,3 +68,35 @@ userRouter.post("/users/signup", async ctx => {
|
|||||||
|
|
||||||
ctx.body = { error: false, data: user.toAuthJSON() };
|
ctx.body = { error: false, data: user.toAuthJSON() };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
userRouter.post("/users/edit", async (ctx) => {
|
||||||
|
if (!ctx.state.user) {
|
||||||
|
ctx.throw(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jwt = ctx.state.user as IUserJWT;
|
||||||
|
const user = await User.findOne(jwt.id);
|
||||||
|
const request = ctx.request as any;
|
||||||
|
|
||||||
|
if (!request.body) {
|
||||||
|
ctx.throw(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { password } = request.body as {
|
||||||
|
password: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
ctx.throw(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
await user.setPassword(password);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await user.save();
|
||||||
|
} catch (e) {
|
||||||
|
ctx.throw(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.body = { error: false, data: user.toAuthJSON() };
|
||||||
|
});
|
||||||
|
|||||||
@@ -102,4 +102,41 @@ describe("users", () => {
|
|||||||
expect(response.body.error).to.be.equal("User already exists");
|
expect(response.body.error).to.be.equal("User already exists");
|
||||||
expect(response.body.data).to.be.false;
|
expect(response.body.data).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should change user's password", async () => {
|
||||||
|
const response = await request(callback)
|
||||||
|
.post("/users/edit")
|
||||||
|
.set({
|
||||||
|
Authorization: `Bearer ${seed.user1.toJWT()}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
})
|
||||||
|
.send({
|
||||||
|
password: "User1NewPass",
|
||||||
|
})
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(response.body.error).to.be.false;
|
||||||
|
|
||||||
|
const loginResponse = await request(callback)
|
||||||
|
.post("/users/login")
|
||||||
|
.set({ "Content-Type": "application/json" })
|
||||||
|
.send({ username: "User1", password: "User1NewPass" })
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(loginResponse.body.error).to.be.false;
|
||||||
|
|
||||||
|
const { jwt: _, ...user } = response.body.data as IUserAuthJSON;
|
||||||
|
expect(user).to.deep.equal(seed.user1.toJSON());
|
||||||
|
|
||||||
|
const badLoginResponse = await request(callback)
|
||||||
|
.post("/users/login")
|
||||||
|
.set({ "Content-Type": "application/json" })
|
||||||
|
.send({ username: "User1", password: "User1" })
|
||||||
|
.expect(404);
|
||||||
|
|
||||||
|
expect(badLoginResponse.body.error).to.be.equal("User not found");
|
||||||
|
expect(badLoginResponse.body.data).to.be.false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user