Adding component to manage account api keys

parent c69be1ac
......@@ -3,6 +3,7 @@ import RoleForm from './components/RoleForm.vue';
import UserTable from './components/UserTable.vue';
import UserForm from './components/UserForm.vue';
import UserProfile from './components/UserProfile.vue';
import ApiKeys from './components/ApiKeys.vue';
const locales = window.AsgardCMS.locales;
......@@ -62,4 +63,9 @@ export default [
name: 'admin.user.users.account',
component: UserProfile,
},
{
path: '/account/api-keys',
name: 'admin.user.users.account.api-keys',
component: ApiKeys,
},
];
<template>
<div>
<div class="content-header">
<h1>
{{ trans('users.api-keys') }}
</h1>
<el-breadcrumb separator="/">
<el-breadcrumb-item>
<a href="/backend">{{ trans('core.breadcrumb.home') }}</a>
</el-breadcrumb-item>
<el-breadcrumb-item :to="{name: 'admin.user.users.account.api-keys'}">{{ trans('users.api-keys') }}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('users.your api keys') }}</h3>
<div class="box-tools pull-right">
<el-button type="primary" size="small" icon="plus" @click="generateKey">
{{ trans('users.generate new api key') }}
</el-button>
</div>
</div>
<div class="box-body">
<ul class="list-unstyled">
<li style="margin-bottom: 20px;"
v-for="key in apiKeys"
:key="key.id">
<el-input v-model="key.access_token" disabled>
<el-button slot="prepend" @click="destroyApiKey(key)">
<i class="fa fa-times"></i>
</el-button>
</el-input>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
loading: false,
apiKeys: {},
};
},
methods: {
fetchApiKeys() {
this.loading = true;
axios.get(route('api.account.api.index'))
.then((response) => {
this.loading = false;
this.apiKeys = response.data.data;
});
},
generateKey() {
axios.get(route('api.account.api.create'))
.then((response) => {
this.loading = false;
this.apiKeys = response.data.data;
this.$message({
type: 'success',
message: response.data.message,
});
});
},
destroyApiKey(apiKey) {
this.$confirm(this.trans('users.delete api key confirm'), '', {
confirmButtonText: this.trans('core.button.delete'),
cancelButtonText: this.trans('core.button.cancel'),
type: 'warning',
confirmButtonClass: 'el-button--danger',
}).then(() => {
axios.delete(route('api.account.api.destroy', { userTokenId: apiKey.id }))
.then((response) => {
this.loading = false;
this.apiKeys = response.data.data;
this.$message({
type: 'success',
message: response.data.message,
});
});
})
.catch(() => {
});
},
},
mounted() {
this.fetchApiKeys();
},
};
</script>
......@@ -28,11 +28,7 @@ class ApiKeysController extends AdminBaseController
public function index()
{
$tokens = $this->userToken->allForUser($this->auth->id());
$this->assetPipeline->requireJs('clipboard.js');
return view('user::admin.account.api-keys.index', compact('tokens'));
return view('user::admin.account.api-keys.index');
}
public function create()
......
<?php
namespace Modules\User\Http\Controllers\Api;
use Illuminate\Routing\Controller;
use Modules\User\Contracts\Authentication;
use Modules\User\Entities\UserToken;
use Modules\User\Repositories\UserTokenRepository;
use Modules\User\Transformers\ApiKeysTransformer;
class ApiKeysController extends Controller
{
/**
* @var Authentication
*/
private $auth;
/**
* @var UserTokenRepository
*/
private $userToken;
public function __construct(Authentication $auth, UserTokenRepository $userToken)
{
$this->auth = $auth;
$this->userToken = $userToken;
}
public function index()
{
$tokens = $this->userToken->allForUser($this->auth->id());
return ApiKeysTransformer::collection($tokens);
}
public function create()
{
$userId = $this->auth->id();
$this->userToken->generateFor($userId);
$tokens = $this->userToken->allForUser($userId);
return response()->json([
'errors' => false,
'message' => trans('user::users.token generated'),
'data' => ApiKeysTransformer::collection($tokens),
]);
}
public function destroy(UserToken $userToken)
{
$this->userToken->destroy($userToken);
$tokens = $this->userToken->allForUser($this->auth->id());
return response()->json([
'errors' => false,
'message' => trans('user::users.token deleted'),
'data' => ApiKeysTransformer::collection($tokens),
]);
}
}
......@@ -95,6 +95,26 @@ $router->group(['prefix' => '/user', 'middleware' => ['api.token', 'auth.admin']
'as' => 'api.account.profile.update',
'uses' => 'ProfileController@update',
]);
$router->bind('userTokenId', function ($id) {
return app(\Modules\User\Repositories\UserTokenRepository::class)->find($id);
});
$router->get('api-keys', [
'as' => 'api.account.api.index',
'uses' => 'ApiKeysController@index',
'middleware' => 'can:account.api-keys.index',
]);
$router->get('api-keys/create', [
'as' => 'api.account.api.create',
'uses' => 'ApiKeysController@create',
'middleware' => 'can:account.api-keys.create',
]);
$router->delete('api-keys/{userTokenId}', [
'as' => 'api.account.api.destroy',
'uses' => 'ApiKeysController@destroy',
'middleware' => 'can:account.api-keys.destroy',
]);
});
$router->get('permissions', [
......
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('user::users.api-keys') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('user::users.api-keys') }}</li>
</ol>
@stop
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="row">
<div class="btn-group pull-right" style="margin: 0 15px 15px 0;">
<a href="{{ route('admin.account.api.create') }}" class="btn btn-primary btn-flat">
<i class="fa fa-plus"></i> {{ trans('user::users.generate new api key') }}
</a>
</div>
</div>
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('user::users.your api keys') }}</h3>
</div>
<div class="box-body">
<div class="col-md-4">
<?php if ($tokens->isEmpty() === false): ?>
<ul class="list-unstyled">
<?php foreach ($tokens as $token): ?>
<li style="margin-bottom: 20px;">
{!! Form::open(['route' => ['admin.account.api.destroy', $token->id], 'method' => 'delete', 'class' => '']) !!}
<div class="input-group input-group-sm">
<span class="input-group-btn">
<button type="submit" class="btn btn-danger btn-flat" onclick="return confirm('{{ trans('user::users.delete api key confirm') }}')">
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</span>
<input type="text" class="form-control api-key" readonly value="{{ $token->access_token }}" >
<span class="input-group-btn">
<a href="#" class="btn btn-default btn-flat jsClipboardButton">
<i class="fa fa-clipboard" aria-hidden="true"></i>
</a>
</span>
</div>
{!! Form::close() !!}
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p>{{ trans('user::users.you have no api keys') }} <a href="{{ route('admin.account.api.create') }}">{{ trans('user::users.generate one') }}</a></p>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- /.col (MAIN) -->
</div>
@include('core::partials.delete-modal')
@stop
@push('js-stack')
<script>
new Clipboard('.jsClipboardButton', {
target: function(trigger) {
return $(trigger).parent().parent().find('.api-key')[0];
}
});
</script>
@endpush
<?php
namespace Modules\User\Transformers;
use Illuminate\Http\Resources\Json\Resource;
class ApiKeysTransformer extends Resource
{
public function toArray($request)
{
return [
'id' => $this->id,
'access_token' => $this->access_token,
'created_at' => $this->created_at,
];
}
}
......@@ -101875,6 +101875,13 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
//
//
//
//
//
//
//
//
//
//
exports.default = {
mixins: [_Slugify2.default, _ShortcutHelper2.default, _ActiveEditor2.default, _SingleFileSelector2.default],
......@@ -102610,6 +102617,47 @@ var render = function() {
1
),
_vm._v(" "),
_c(
"el-form-item",
{
class: {
"el-form-item is-error": _vm.form.errors.has(
locale + ".status"
)
},
attrs: { label: _vm.trans("page.status") }
},
[
_c(
"el-checkbox",
{
model: {
value: _vm.page[locale].status,
callback: function($$v) {
_vm.page[locale].status = $$v
},
expression: "page[locale].status"
}
},
[_vm._v(_vm._s(_vm.trans("page.status")))]
),
_vm._v(" "),
_vm.form.errors.has(locale + ".status")
? _c("div", {
staticClass: "el-form-item__error",
domProps: {
textContent: _vm._s(
_vm.form.errors.first(
locale + ".status"
)
)
}
})
: _vm._e()
],
1
),
_vm._v(" "),
_c(
"div",
{ staticClass: "panel box box-primary" },
......@@ -103026,7 +103074,7 @@ var render = function() {
attrs: {
zone: "image",
entity: "Modules\\Page\\Entities\\Page",
"entity-id": _vm.page.id
"entity-id": _vm.$route.params.pageId
},
on: {
singleFileSelected: function($event) {
......@@ -105829,6 +105877,10 @@ var _UserProfile = __webpack_require__(520);
var _UserProfile2 = _interopRequireDefault(_UserProfile);
var _ApiKeys = __webpack_require__(562);
var _ApiKeys2 = _interopRequireDefault(_ApiKeys);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var locales = window.AsgardCMS.locales;
......@@ -105883,6 +105935,10 @@ exports.default = [
path: '/account/profile',
name: 'admin.user.users.account',
component: _UserProfile2.default
}, {
path: '/account/api-keys',
name: 'admin.user.users.account.api-keys',
component: _ApiKeys2.default
}];
/***/ }),
......@@ -109347,7 +109403,7 @@ var render = function() {
_vm._v(" "),
_c(
"el-breadcrumb-item",
{ attrs: { to: { name: "admin.user.users.profile" } } },
{ attrs: { to: { name: "admin.user.users.account" } } },
[
_vm._v(
_vm._s(_vm.trans("users.breadcrumb.edit-profile")) +
......@@ -110572,7 +110628,7 @@ exports.default = {
props: {
zone: { type: String, required: true },
entity: { type: String, required: true },
entityId: { type: Number },
entityId: { default: null },
label: { type: String }
},
components: {
......@@ -110636,6 +110692,9 @@ exports.default = {
mounted: function mounted() {
var _this2 = this;
if (this.entityId) {
this.fetchMedia();
}
this.eventName = 'fileWasSelected' + this.makeId() + Math.floor(Math.random() * 999999);
this.$events.listen(this.eventName, function (mediaData) {
......@@ -110985,5 +111044,313 @@ if (false) {
// removed by extract-text-webpack-plugin
/***/ }),
/* 550 */,
/* 551 */,
/* 552 */,
/* 553 */,
/* 554 */,
/* 555 */,
/* 556 */,
/* 557 */,
/* 558 */,
/* 559 */,
/* 560 */,
/* 561 */,
/* 562 */
/***/ (function(module, exports, __webpack_require__) {
var disposed = false
var normalizeComponent = __webpack_require__(6)
/* script */
var __vue_script__ = __webpack_require__(563)
/* template */
var __vue_template__ = __webpack_require__(564)
/* template functional */
var __vue_template_functional__ = false
/* styles */
var __vue_styles__ = null
/* scopeId */
var __vue_scopeId__ = null
/* moduleIdentifier (server only) */
var __vue_module_identifier__ = null
var Component = normalizeComponent(
__vue_script__,
__vue_template__,
__vue_template_functional__,
__vue_styles__,
__vue_scopeId__,
__vue_module_identifier__
)
Component.options.__file = "Modules/User/Assets/js/components/ApiKeys.vue"
if (Component.esModule && Object.keys(Component.esModule).some(function (key) { return key !== "default" && key.substr(0, 2) !== "__"})) { console.error("named exports are not supported in *.vue files.")}
/* hot reload */
if (false) {(function () {
var hotAPI = require("vue-hot-reload-api")
hotAPI.install(require("vue"), false)
if (!hotAPI.compatible) return
module.hot.accept()
if (!module.hot.data) {
hotAPI.createRecord("data-v-66a1f586", Component.options)
} else {
hotAPI.reload("data-v-66a1f586", Component.options)
' + ' }
module.hot.dispose(function (data) {
disposed = true
})
})()}
module.exports = Component.exports
/***/ }),
/* 563 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _axios = __webpack_require__(12);
var _axios2 = _interopRequireDefault(_axios);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = {
data: function data() {
return {
loading: false,
apiKeys: {}
};
},
methods: {
fetchApiKeys: function fetchApiKeys() {
var _this = this;
this.loading = true;
_axios2.default.get(route('api.account.api.index')).then(function (response) {
_this.loading = false;
_this.apiKeys = response.data.data;
});
},
generateKey: function generateKey() {
var _this2 = this;
_axios2.default.get(route('api.account.api.create')).then(function (response) {
_this2.loading = false;
_this2.apiKeys = response.data.data;
_this2.$message({
type: 'success',
message: response.data.message
});
});
},
destroyApiKey: function destroyApiKey(apiKey) {
var _this3 = this;
this.$confirm(this.trans('users.delete api key confirm'), '', {
confirmButtonText: this.trans('core.button.delete'),
cancelButtonText: this.trans('core.button.cancel'),
type: 'warning',
confirmButtonClass: 'el-button--danger'
}).then(function () {
_axios2.default.delete(route('api.account.api.destroy', { userTokenId: apiKey.id })).then(function (response) {
_this3.loading = false;
_this3.apiKeys = response.data.data;
_this3.$message({
type: 'success',
message: response.data.message
});
});
}).catch(function () {});
}
},
mounted: function mounted() {
this.fetchApiKeys();
}
}; //
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
/***/ }),
/* 564 */
/***/ (function(module, exports, __webpack_require__) {
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c("div", [
_c(
"div",
{ staticClass: "content-header" },
[
_c("h1", [
_vm._v(
"\n " +
_vm._s(_vm.trans("users.api-keys")) +
"\n "
)
]),
_vm._v(" "),
_c(
"el-breadcrumb",
{ attrs: { separator: "/" } },
[
_c("el-breadcrumb-item", [
_c("a", { attrs: { href: "/backend" } }, [
_vm._v(_vm._s(_vm.trans("core.breadcrumb.home")))
])
]),
_vm._v(" "),
_c(
"el-breadcrumb-item",
{ attrs: { to: { name: "admin.user.users.account.api-keys" } } },
[_vm._v(_vm._s(_vm.trans("users.api-keys")) + "\n ")]
)
],
1
)
],
1
),
_vm._v(" "),
_c("div", { staticClass: "row" }, [
_c("div", { staticClass: "col-md-12" }, [
_c("div", { staticClass: "box box-primary" }, [
_c("div", { staticClass: "box-header with-border" }, [
_c("h3", { staticClass: "box-title" }, [
_vm._v(_vm._s(_vm.trans("users.your api keys")))
]),
_vm._v(" "),
_c(
"div",
{ staticClass: "box-tools pull-right" },
[
_c(
"el-button",
{
attrs: { type: "primary", size: "small", icon: "plus" },
on: { click: _vm.generateKey }
},
[
_vm._v(
"\n " +
_vm._s(_vm.trans("users.generate new api key")) +
"\n "
)
]
)
],
1
)
]),
_vm._v(" "),
_c("div", { staticClass: "box-body" }, [
_c(
"ul",
{ staticClass: "list-unstyled" },
_vm._l(_vm.apiKeys, function(key) {
return _c(
"li",
{ key: key.id, staticStyle: { "margin-bottom": "20px" } },
[
_c(
"el-input",
{
attrs: { disabled: "" },
model: {
value: key.access_token,
callback: function($$v) {
key.access_token = $$v
},
expression: "key.access_token"
}
},
[
_c(
"el-button",
{
attrs: { slot: "prepend" },
on: {
click: function($event) {
_vm.destroyApiKey(key)
}
},
slot: "prepend"
},
[_c("i", { staticClass: "fa fa-times" })]
)
],
1
)
],
1
)
})
)
])
])
])
])
])
}
var staticRenderFns = []
render._withStripped = true
module.exports = { render: render, staticRenderFns: staticRenderFns }
if (false) {
module.hot.accept()
if (module.hot.data) {
require("vue-hot-reload-api") .rerender("data-v-66a1f586", module.exports)
}
}
/***/ })
/******/ ]);
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment