Adding Delete user button and basic functionality

This commit is contained in:
Aran Zaiger 2020-05-23 22:02:04 +03:00
parent 4d934fbcdf
commit 3bfa60db28
11 changed files with 336 additions and 295 deletions

View file

@ -6,35 +6,50 @@ const IceServer = use('App/Models/IceServer');
const EmailUtils = use('App/Utils/EmailUtils');
class AdminApiController {
async getUsers({response}) {
const users = await User.all();
// console.log(typeof users);
// return users.rows.map(u => {
// return u.publicJSON();
// });
return users;
}
async addStunServer({request, response}) {}
async addTurnServer({request, response}) {}
async testEmailSettings({auth, response}) {
try {
if (EmailUtils.sendTestEmail(auth.user)) {
return {
code: 0, data: {}
}
}
return {
code: 500, message: 'Something went wrong'
}
} catch (e) {
response.code(500);
return {
code: 500, message: e.message
}
async getUsers({ response }) {
const users = await User.all();
// console.log(typeof users);
// return users.rows.map(u => {
// return u.publicJSON();
// });
return users;
}
async deleteUser({ request, response }) {
console.log('in delete user')
const { id } = request.params
const user = await User.find(id)
let userLinks = await user.links().fetch();
const links = userLinks.rows
const promises = [...links.map(l => (l.delete())), user.delete()];
return await Promise.all(promises);
}
async addStunServer({ request, response }) {}
async addTurnServer({ request, response }) {}
async testEmailSettings({ auth, response }) {
try {
if (EmailUtils.sendTestEmail(auth.user)) {
return {
code: 0,
data: {}
}
}
return {
code: 500,
message: 'Something went wrong'
}
} catch (e) {
response.code(500);
return {
code: 500,
message: e.message
}
}
}
}
}
module.exports = AdminApiController

View file

@ -7,54 +7,54 @@ const Hash = use('Hash')
const Model = use('Model')
class User extends Model {
static boot() {
super
.boot()
static boot() {
super
.boot()
/**
* A hook to hash the user password before saving
* it to the database.
*/
this.addHook('beforeSave', async (userInstance) => {
if (userInstance.dirty.password) {
userInstance.password = await Hash.make(userInstance.password)
}
this.addHook('beforeSave', async(userInstance) => {
if (userInstance.dirty.password) {
userInstance.password = await Hash.make(userInstance.password)
}
})
}
publicJSON() {
const u = this.toJSON();
return {
avatar: `https://api.adorable.io/avatars/285/${u.email}.png`, id: u.id,
name: u.name, isAdmin: u.is_admin
}
}
static get hidden() {
return ['password']
}
// publicJSON() {
// const u = this.toJSON();
// return {
// avatar: `https://api.adorable.io/avatars/285/${u.email}.png`, id: u.id,
// name: u.name, isAdmin: u.is_admin
// }
// }
static get dates() {
return super.dates.concat(['last_logged_in'])
}
static get hidden() {
return ['password']
}
/**
* A relationship on tokens is required for auth to
* work. Since features like `refreshTokens` or
* `rememberToken` will be saved inside the
* tokens table.
*
* @method tokens
*
* @return {Object}
*/
tokens() {
return this.hasMany('App/Models/Token')
}
static get dates() {
return super.dates.concat(['last_logged_in'])
}
links() {
return this.hasMany('App/Models/Link')
}
/**
* A relationship on tokens is required for auth to
* work. Since features like `refreshTokens` or
* `rememberToken` will be saved inside the
* tokens table.
*
* @method tokens
*
* @return {Object}
*/
tokens() {
return this.hasMany('App/Models/Token')
}
links() {
return this.hasMany('App/Models/Link')
}
}
module.exports = User

View file

@ -1,140 +1,140 @@
'use strict'
module.exports = {
/*
|--------------------------------------------------------------------------
| Content Security Policy
|--------------------------------------------------------------------------
|
| Content security policy filters out the origins not allowed to execute
| and load resources like scripts, styles and fonts. There are wide
| variety of options to choose from.
*/
csp: {
/*
|--------------------------------------------------------------------------
| Directives
| Content Security Policy
|--------------------------------------------------------------------------
|
| All directives are defined in camelCase and here is the list of
| available directives and their possible values.
|
| https://content-security-policy.com
|
| @example
| directives: {
| defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com']
| }
|
| Content security policy filters out the origins not allowed to execute
| and load resources like scripts, styles and fonts. There are wide
| variety of options to choose from.
*/
directives: {},
/*
|--------------------------------------------------------------------------
| Report only
|--------------------------------------------------------------------------
|
| Setting `reportOnly=true` will not block the scripts from running and
| instead report them to a URL.
|
*/
reportOnly: false,
/*
|--------------------------------------------------------------------------
| Set all headers
|--------------------------------------------------------------------------
|
| Headers staring with `X` have been depreciated, since all major browsers
| supports the standard CSP header. So its better to disable deperciated
| headers, unless you want them to be set.
|
*/
setAllHeaders: false,
csp: {
/*
|--------------------------------------------------------------------------
| Directives
|--------------------------------------------------------------------------
|
| All directives are defined in camelCase and here is the list of
| available directives and their possible values.
|
| https://content-security-policy.com
|
| @example
| directives: {
| defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com']
| }
|
*/
directives: {},
/*
|--------------------------------------------------------------------------
| Report only
|--------------------------------------------------------------------------
|
| Setting `reportOnly=true` will not block the scripts from running and
| instead report them to a URL.
|
*/
reportOnly: false,
/*
|--------------------------------------------------------------------------
| Set all headers
|--------------------------------------------------------------------------
|
| Headers staring with `X` have been depreciated, since all major browsers
| supports the standard CSP header. So its better to disable deperciated
| headers, unless you want them to be set.
|
*/
setAllHeaders: false,
/*
|--------------------------------------------------------------------------
| Disable on android
|--------------------------------------------------------------------------
|
| Certain versions of android are buggy with CSP policy. So you can set
| this value to true, to disable it for Android versions with buggy
| behavior.
|
| Here is an issue reported on a different package, but helpful to read
| if you want to know the behavior.
https://github.com/helmetjs/helmet/pull/82
|
*/
disableAndroid: true
},
/*
|--------------------------------------------------------------------------
| Disable on android
| X-XSS-Protection
|--------------------------------------------------------------------------
|
| Certain versions of android are buggy with CSP policy. So you can set
| this value to true, to disable it for Android versions with buggy
| behavior.
| X-XSS Protection saves applications from XSS attacks. It is adopted
| by IE and later followed by some other browsers.
|
| Here is an issue reported on a different package, but helpful to read
| if you want to know the behavior.
https://github.com/helmetjs/helmet/pull/82
| Learn more at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
*/
disableAndroid: true
},
xss: { enabled: true, enableOnOldIE: false },
/*
|--------------------------------------------------------------------------
| X-XSS-Protection
|--------------------------------------------------------------------------
|
| X-XSS Protection saves applications from XSS attacks. It is adopted
| by IE and later followed by some other browsers.
|
| Learn more at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
*/
xss: {enabled: true, enableOnOldIE: false},
/*
|--------------------------------------------------------------------------
| Iframe Options
|--------------------------------------------------------------------------
|
| xframe defines whether or not your website can be embedded inside an
| iframe. Choose from one of the following options.
| @available options
| DENY, SAMEORIGIN, ALLOW-FROM http://example.com
|
| Learn more at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
*/
xframe: 'DENY',
/*
|--------------------------------------------------------------------------
| Iframe Options
|--------------------------------------------------------------------------
|
| xframe defines whether or not your website can be embedded inside an
| iframe. Choose from one of the following options.
| @available options
| DENY, SAMEORIGIN, ALLOW-FROM http://example.com
|
| Learn more at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
*/
xframe: 'DENY',
/*
|--------------------------------------------------------------------------
| No Sniff
|--------------------------------------------------------------------------
|
| Browsers have a habit of sniffing content-type of a response. Which means
| files with .txt extension containing Javascript code will be executed as
| Javascript. You can disable this behavior by setting nosniff to false.
|
| Learn more at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
*/
nosniff: true,
/*
|--------------------------------------------------------------------------
| No Sniff
|--------------------------------------------------------------------------
|
| Browsers have a habit of sniffing content-type of a response. Which means
| files with .txt extension containing Javascript code will be executed as
| Javascript. You can disable this behavior by setting nosniff to false.
|
| Learn more at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
*/
nosniff: true,
/*
|--------------------------------------------------------------------------
| No Open
|--------------------------------------------------------------------------
|
| IE users can execute webpages in the context of your website, which is
| a serious security risk. Below option will manage this for you.
|
*/
noopen: true,
/*
|--------------------------------------------------------------------------
| No Open
|--------------------------------------------------------------------------
|
| IE users can execute webpages in the context of your website, which is
| a serious security risk. Below option will manage this for you.
|
*/
noopen: true,
/*
|--------------------------------------------------------------------------
| CSRF Protection
|--------------------------------------------------------------------------
|
| CSRF Protection adds another layer of security by making sure, actionable
| routes does have a valid token to execute an action.
|
*/
csrf: {
enable: true,
methods: ['POST', 'PUT', 'DELETE'],
filterUris: [/api\/v1\/client\/\w+/], // All Client API routes
cookieOptions: {httpOnly: false, sameSite: true, path: '/', maxAge: 7200}
}
/*
|--------------------------------------------------------------------------
| CSRF Protection
|--------------------------------------------------------------------------
|
| CSRF Protection adds another layer of security by making sure, actionable
| routes does have a valid token to execute an action.
|
*/
csrf: {
enable: true,
methods: ['POST', 'PUT', 'DELETE'],
filterUris: [/api\/v1\/client\/\w+/, /api\/v1\/admin\/\w+/], // All Client API routes
cookieOptions: { httpOnly: false, sameSite: true, path: '/', maxAge: 7200 }
}
}

View file

@ -1,77 +1,78 @@
{
"name": "Seepur",
"version": "0.0.1",
"adonis-version": "4.1.0",
"description": "A shared story time experience",
"main": "server.js",
"scripts": {
"build:css": "npx node-sass --omit-source-map-url resources/sass/main.scss public/style.css",
"build:applications": "npx webpack --mode production",
"watch:css": "npm run build:css -- --watch",
"watch:applications": "npx webpack --watch",
"migrate": "npx adonis migration:run -f",
"build": "npm run migrate && npm run build:css && npm run build:applications",
"start": "npm run migrate && node server.js",
"clean": "bash clean-hot-update.sh",
"test": "node ace test"
},
"keywords": [
"seepur"
],
"author": "",
"license": "UNLICENSED",
"private": true,
"dependencies": {
"@adonisjs/ace": "^5.0.8",
"@adonisjs/auth": "^3.0.7",
"@adonisjs/bodyparser": "^2.0.5",
"@adonisjs/cli": "^4.0.12",
"@adonisjs/cors": "^1.0.7",
"@adonisjs/drive": "^1.0.4",
"@adonisjs/fold": "^4.0.9",
"@adonisjs/framework": "^5.0.9",
"@adonisjs/ignitor": "^2.0.8",
"@adonisjs/lucid": "^6.1.3",
"@adonisjs/mail": "^3.0.10",
"@adonisjs/redis": "^2.0.7",
"@adonisjs/session": "^1.0.27",
"@adonisjs/shield": "^1.0.8",
"@adonisjs/validator": "^5.0.6",
"@adonisjs/websocket": "^1.0.12",
"@adonisjs/websocket-client": "^1.0.9",
"adonis-vue-websocket": "^2.0.2",
"animate.css": "^4.1.0",
"bulma": "^0.8.0",
"fork-awesome": "^1.1.7",
"moment": "^2.24.0",
"regenerator-runtime": "^0.13.5",
"rematrix": "^0.7.0",
"sqlite3": "^4.1.1",
"typescript": "^3.7.5",
"uuid": "^8.0.0",
"vue": "^2.6.11",
"vue-croppa": "^1.3.8",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/plugin-transform-regenerator": "^7.8.7",
"@babel/plugin-transform-runtime": "^7.9.0",
"@types/node": "^14.0.1",
"babel-loader": "^8.0.6",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
"css-loader": "^3.4.2",
"node-sass": "^4.13.0",
"ts-loader": "^7.0.4",
"vue-loader": "^15.8.3",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
},
"autoload": {
"App": "./app"
}
"name": "Seepur",
"version": "0.0.1",
"adonis-version": "4.1.0",
"description": "A shared story time experience",
"main": "server.js",
"scripts": {
"build:css": "npx node-sass --omit-source-map-url resources/sass/main.scss public/style.css",
"build:applications": "npx webpack --mode production",
"watch:css": "npm run build:css -- --watch",
"watch:applications": "npx webpack --watch",
"migrate": "npx adonis migration:run -f",
"build": "npm run migrate && npm run build:css && npm run build:applications",
"start": "npm run migrate && node server.js",
"start:dev": "npx adonis serve --dev",
"clean": "bash clean-hot-update.sh",
"test": "node ace test"
},
"keywords": [
"seepur"
],
"author": "",
"license": "UNLICENSED",
"private": true,
"dependencies": {
"@adonisjs/ace": "^5.0.8",
"@adonisjs/auth": "^3.0.7",
"@adonisjs/bodyparser": "^2.0.5",
"@adonisjs/cli": "^4.0.12",
"@adonisjs/cors": "^1.0.7",
"@adonisjs/drive": "^1.0.4",
"@adonisjs/fold": "^4.0.9",
"@adonisjs/framework": "^5.0.9",
"@adonisjs/ignitor": "^2.0.8",
"@adonisjs/lucid": "^6.1.3",
"@adonisjs/mail": "^3.0.10",
"@adonisjs/redis": "^2.0.7",
"@adonisjs/session": "^1.0.27",
"@adonisjs/shield": "^1.0.8",
"@adonisjs/validator": "^5.0.6",
"@adonisjs/websocket": "^1.0.12",
"@adonisjs/websocket-client": "^1.0.9",
"adonis-vue-websocket": "^2.0.2",
"animate.css": "^4.1.0",
"bulma": "^0.8.0",
"fork-awesome": "^1.1.7",
"moment": "^2.24.0",
"regenerator-runtime": "^0.13.5",
"rematrix": "^0.7.0",
"sqlite3": "^4.1.1",
"typescript": "^3.7.5",
"uuid": "^8.0.0",
"vue": "^2.6.11",
"vue-croppa": "^1.3.8",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/plugin-transform-regenerator": "^7.8.7",
"@babel/plugin-transform-runtime": "^7.9.0",
"@types/node": "^14.0.1",
"babel-loader": "^8.0.6",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
"css-loader": "^3.4.2",
"node-sass": "^4.13.0",
"ts-loader": "^7.0.4",
"vue-loader": "^15.8.3",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
},
"autoload": {
"App": "./app"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -3,6 +3,9 @@
<Modal title="CreateUser" :isActive="showCreateUser" @close="showCreateUser=false" acceptText="Create" @accept="createUser()">
test
</Modal>
<Modal title="DeleteUser" :isActive="showDeleteUser" @close="showDeleteUser=false; currentUser=null" acceptText="Delete" rejectText="Cancel" @accept="deleteUser(currentUser)">
Are you sure you want to delete {{user.name}}?
</Modal>
<nav class="level">
<div class="level-left">
<div class="level-item">
@ -35,6 +38,7 @@
<th>name</th>
<th>email</th>
<th>admin</th>
<th>edit</th>
</tr>
</thead>
<tr v-for="user in users" :key="user.id">
@ -49,13 +53,15 @@
<td>
<input class="checkbox" type="checkbox" :checked="user.is_admin" />
</td>
<td>
<button v-if="!user.is_admin" class="button" @click="onDeleteClicked(user)">Delete</button>
</td>
</tr>
</table>
</div>
</template>
<script lang="ts">
import ChildAvatar, { IChildAvatar } from "../components/child_avatar.vue";
import Services from "../../services/index";
import { mapGetters, mapActions } from "vuex";
import Modal from "../../shared/components/Modal/Modal.vue";
@ -63,22 +69,28 @@ import Modal from "../../shared/components/Modal/Modal.vue";
export default {
name: "Home",
components: {
ChildAvatar,
Modal
},
methods: {
createUser(){
alert('created');
},
async deleteUser(user){
console.log(user)
await Services.ApiService.deleteUser(user);
this.showDeleteUser=false;
await this.getUsers()
},
onDeleteClicked(user){
this.showDeleteUser = true;
this.currentUser = user;
},
...mapActions(["getUsers"])
},
async created() {
this.loading = true;
if (this.users === null) await this.getUsers();
this.loading = false;
// this.connections = await Services.ApiService.getConnections();
// this.users = await Services.ApiService.getAllUsers();
// console.dir(connections);
},
computed: {
// async users() {
@ -89,6 +101,8 @@ export default {
return {
loading: true,
showCreateUser: false,
showDeleteUser: false,
currentUser: null,
};
}
};

View file

@ -1,5 +1,15 @@
export default class ApiService {
static async deleteUser(user: any) {
try{
return (await fetch(`/api/v1/admin/user/${user.id}`, {method: 'DELETE'})).json();
}
catch (e) {
console.error(`deleteUser ERROR: ${e.message}`);
return e;
}
}
static async getUser(userId?: number) {
try {
return (await fetch('/api/v1/client/user/')).json();

View file

@ -28,13 +28,13 @@ Route.post('/login', 'AuthController.login').validator('Login');
// reset-password
Route.post('/password/request/reset', 'AuthController.resetPasswordRequest')
.validator('ResetPasswordRequest')
.as('resetPasswordRequest');
.validator('ResetPasswordRequest')
.as('resetPasswordRequest');
Route.get('/password/reset', 'AuthController.resetPasswordRequestIndex');
Route.get('/password/reset/:token', 'AuthController.resetPasswordIndex');
Route.post('/password/reset', 'AuthController.resetPassword')
.validator('ResetPassword')
.as('resetPassword');
.validator('ResetPassword')
.as('resetPassword');
/*
/ Client API
@ -42,12 +42,12 @@ Route.post('/password/reset', 'AuthController.resetPassword')
Route
.group(() => {
Route.post('connections/create', 'ClientApiController.createConnection');
Route.get('user', 'ClientApiController.getUser');
Route.post('child', 'ClientApiController.createChild');
Route.get('child/:id', 'ClientApiController.getChild');
Route.post('child/:id', 'ClientApiController.updateChild');
Route.post('call/create', 'ClientApiController.createCall');
Route.post('connections/create', 'ClientApiController.createConnection');
Route.get('user', 'ClientApiController.getUser');
Route.post('child', 'ClientApiController.createChild');
Route.get('child/:id', 'ClientApiController.getChild');
Route.post('child/:id', 'ClientApiController.updateChild');
Route.post('call/create', 'ClientApiController.createCall');
})
.prefix('api/v1/client')
.middleware(['auth']);
@ -65,9 +65,9 @@ Route
*/
Route.get('/u/books/:bookId/thumbnail', 'BookApiController.getThumbnail')
.middleware(['auth', 'BookContext'])
/*
/ Pubic CDN Images
*/
/*
/ Pubic CDN Images
*/
Route.get('/u/images/:fileName', 'CdnController.publicImages');
/*
@ -76,16 +76,17 @@ Route.get('/u/images/:fileName', 'CdnController.publicImages');
// API
Route
.group(() => {
Route.get('users', 'AdminApiController.getUsers');
Route.get(
'settings/email/test/result', 'AdminApiController.testEmailSettings');
Route.get('users', 'AdminApiController.getUsers');
Route.delete('user/:id', 'AdminApiController.deleteUser');
Route.get(
'settings/email/test/result', 'AdminApiController.testEmailSettings');
})
.prefix('/api/v1/admin')
.middleware(['auth', 'adminAuth']);
Route
.group(() => {
//
Route.get('/*', 'AdminController.index');
//
Route.get('/*', 'AdminController.index');
})
.prefix('admin')
.middleware(['auth', 'adminAuth']);