瀏覽代碼

Add Laravel Passport vue components

Daniel Supernault 7 年之前
父節點
當前提交
5ec4b8f8d2

+ 107 - 0
resources/assets/js/components/passport/AuthorizedClients.vue

@@ -0,0 +1,107 @@
+<style scoped>
+    .action-link {
+        cursor: pointer;
+    }
+</style>
+
+<template>
+    <div>
+        <div v-if="tokens.length > 0">
+            <div class="card card-default mb-4">
+                <div class="card-header font-weight-bold bg-white">Authorized Applications</div>
+
+                <div class="card-body">
+                    <!-- Authorized Tokens -->
+                    <table class="table table-borderless mb-0">
+                        <thead>
+                            <tr>
+                                <th>Name</th>
+                                <th>Scopes</th>
+                                <th></th>
+                            </tr>
+                        </thead>
+
+                        <tbody>
+                            <tr v-for="token in tokens">
+                                <!-- Client Name -->
+                                <td style="vertical-align: middle;">
+                                    {{ token.client.name }}
+                                </td>
+
+                                <!-- Scopes -->
+                                <td style="vertical-align: middle;">
+                                    <span v-if="token.scopes.length > 0">
+                                        {{ token.scopes.join(', ') }}
+                                    </span>
+                                </td>
+
+                                <!-- Revoke Button -->
+                                <td style="vertical-align: middle;">
+                                    <a class="action-link text-danger" @click="revoke(token)">
+                                        Revoke
+                                    </a>
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        /*
+         * The component's data.
+         */
+        data() {
+            return {
+                tokens: []
+            };
+        },
+
+        /**
+         * Prepare the component (Vue 1.x).
+         */
+        ready() {
+            this.prepareComponent();
+        },
+
+        /**
+         * Prepare the component (Vue 2.x).
+         */
+        mounted() {
+            this.prepareComponent();
+        },
+
+        methods: {
+            /**
+             * Prepare the component (Vue 2.x).
+             */
+            prepareComponent() {
+                this.getTokens();
+            },
+
+            /**
+             * Get all of the authorized tokens for the user.
+             */
+            getTokens() {
+                axios.get('/oauth/tokens')
+                        .then(response => {
+                            this.tokens = response.data;
+                        });
+            },
+
+            /**
+             * Revoke the given token.
+             */
+            revoke(token) {
+                axios.delete('/oauth/tokens/' + token.id)
+                        .then(response => {
+                            this.getTokens();
+                        });
+            }
+        }
+    }
+</script>

+ 350 - 0
resources/assets/js/components/passport/Clients.vue

@@ -0,0 +1,350 @@
+<style scoped>
+    .action-link {
+        cursor: pointer;
+    }
+</style>
+
+<template>
+    <div>
+        <div class="card card-default mb-4">
+            <div class="card-header font-weight-bold bg-white">
+                <div style="display: flex; justify-content: space-between; align-items: center;">
+                    <span>
+                        OAuth Clients
+                    </span>
+
+                    <a class="action-link" tabindex="-1" @click="showCreateClientForm">
+                        Create New Client
+                    </a>
+                </div>
+            </div>
+
+            <div class="card-body">
+                <!-- Current Clients -->
+                <p class="mb-0" v-if="clients.length === 0">
+                    You have not created any OAuth clients.
+                </p>
+
+                <table class="table table-borderless mb-0" v-if="clients.length > 0">
+                    <thead>
+                        <tr>
+                            <th>Client ID</th>
+                            <th>Name</th>
+                            <th>Secret</th>
+                            <th></th>
+                            <th></th>
+                        </tr>
+                    </thead>
+
+                    <tbody>
+                        <tr v-for="client in clients">
+                            <!-- ID -->
+                            <td style="vertical-align: middle;">
+                                {{ client.id }}
+                            </td>
+
+                            <!-- Name -->
+                            <td style="vertical-align: middle;">
+                                {{ client.name }}
+                            </td>
+
+                            <!-- Secret -->
+                            <td style="vertical-align: middle;">
+                                <code>{{ client.secret }}</code>
+                            </td>
+
+                            <!-- Edit Button -->
+                            <td style="vertical-align: middle;">
+                                <a class="action-link" tabindex="-1" @click="edit(client)">
+                                    Edit
+                                </a>
+                            </td>
+
+                            <!-- Delete Button -->
+                            <td style="vertical-align: middle;">
+                                <a class="action-link text-danger" @click="destroy(client)">
+                                    Delete
+                                </a>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+
+        <!-- Create Client Modal -->
+        <div class="modal fade" id="modal-create-client" tabindex="-1" role="dialog">
+            <div class="modal-dialog">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h4 class="modal-title">
+                            Create Client
+                        </h4>
+
+                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    </div>
+
+                    <div class="modal-body">
+                        <!-- Form Errors -->
+                        <div class="alert alert-danger" v-if="createForm.errors.length > 0">
+                            <p class="mb-0"><strong>Whoops!</strong> Something went wrong!</p>
+                            <br>
+                            <ul>
+                                <li v-for="error in createForm.errors">
+                                    {{ error }}
+                                </li>
+                            </ul>
+                        </div>
+
+                        <!-- Create Client Form -->
+                        <form role="form">
+                            <!-- Name -->
+                            <div class="form-group row">
+                                <label class="col-md-3 col-form-label">Name</label>
+
+                                <div class="col-md-9">
+                                    <input id="create-client-name" type="text" class="form-control" autocomplete="off"
+                                                                @keyup.enter="store" v-model="createForm.name">
+
+                                    <span class="form-text text-muted">
+                                        Something your users will recognize and trust.
+                                    </span>
+                                </div>
+                            </div>
+
+                            <!-- Redirect URL -->
+                            <div class="form-group row">
+                                <label class="col-md-3 col-form-label">Redirect URL</label>
+
+                                <div class="col-md-9">
+                                    <input type="text" class="form-control" name="redirect"
+                                                    @keyup.enter="store" v-model="createForm.redirect">
+
+                                    <span class="form-text text-muted">
+                                        Your application's authorization callback URL.
+                                    </span>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+
+                    <!-- Modal Actions -->
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-secondary font-weight-bold" data-dismiss="modal">Close</button>
+
+                        <button type="button" class="btn btn-primary font-weight-bold" @click="store">
+                            Create
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- Edit Client Modal -->
+        <div class="modal fade" id="modal-edit-client" tabindex="-1" role="dialog">
+            <div class="modal-dialog">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h4 class="modal-title">
+                            Edit Client
+                        </h4>
+
+                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    </div>
+
+                    <div class="modal-body">
+                        <!-- Form Errors -->
+                        <div class="alert alert-danger" v-if="editForm.errors.length > 0">
+                            <p class="mb-0"><strong>Whoops!</strong> Something went wrong!</p>
+                            <br>
+                            <ul>
+                                <li v-for="error in editForm.errors">
+                                    {{ error }}
+                                </li>
+                            </ul>
+                        </div>
+
+                        <!-- Edit Client Form -->
+                        <form role="form">
+                            <!-- Name -->
+                            <div class="form-group row">
+                                <label class="col-md-3 col-form-label">Name</label>
+
+                                <div class="col-md-9">
+                                    <input id="edit-client-name" type="text" class="form-control"
+                                                                @keyup.enter="update" v-model="editForm.name">
+
+                                    <span class="form-text text-muted">
+                                        Something your users will recognize and trust.
+                                    </span>
+                                </div>
+                            </div>
+
+                            <!-- Redirect URL -->
+                            <div class="form-group row">
+                                <label class="col-md-3 col-form-label">Redirect URL</label>
+
+                                <div class="col-md-9">
+                                    <input type="text" class="form-control" name="redirect"
+                                                    @keyup.enter="update" v-model="editForm.redirect">
+
+                                    <span class="form-text text-muted">
+                                        Your application's authorization callback URL.
+                                    </span>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+
+                    <!-- Modal Actions -->
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+
+                        <button type="button" class="btn btn-primary" @click="update">
+                            Save Changes
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        /*
+         * The component's data.
+         */
+        data() {
+            return {
+                clients: [],
+
+                createForm: {
+                    errors: [],
+                    name: '',
+                    redirect: ''
+                },
+
+                editForm: {
+                    errors: [],
+                    name: '',
+                    redirect: ''
+                }
+            };
+        },
+
+        /**
+         * Prepare the component (Vue 1.x).
+         */
+        ready() {
+            this.prepareComponent();
+        },
+
+        /**
+         * Prepare the component (Vue 2.x).
+         */
+        mounted() {
+            this.prepareComponent();
+        },
+
+        methods: {
+            /**
+             * Prepare the component.
+             */
+            prepareComponent() {
+                this.getClients();
+
+                $('#modal-create-client').on('shown.bs.modal', () => {
+                    $('#create-client-name').focus();
+                });
+
+                $('#modal-edit-client').on('shown.bs.modal', () => {
+                    $('#edit-client-name').focus();
+                });
+            },
+
+            /**
+             * Get all of the OAuth clients for the user.
+             */
+            getClients() {
+                axios.get('/oauth/clients')
+                        .then(response => {
+                            this.clients = response.data;
+                        });
+            },
+
+            /**
+             * Show the form for creating new clients.
+             */
+            showCreateClientForm() {
+                $('#modal-create-client').modal('show');
+            },
+
+            /**
+             * Create a new OAuth client for the user.
+             */
+            store() {
+                this.persistClient(
+                    'post', '/oauth/clients',
+                    this.createForm, '#modal-create-client'
+                );
+            },
+
+            /**
+             * Edit the given client.
+             */
+            edit(client) {
+                this.editForm.id = client.id;
+                this.editForm.name = client.name;
+                this.editForm.redirect = client.redirect;
+
+                $('#modal-edit-client').modal('show');
+            },
+
+            /**
+             * Update the client being edited.
+             */
+            update() {
+                this.persistClient(
+                    'put', '/oauth/clients/' + this.editForm.id,
+                    this.editForm, '#modal-edit-client'
+                );
+            },
+
+            /**
+             * Persist the client to storage using the given form.
+             */
+            persistClient(method, uri, form, modal) {
+                form.errors = [];
+
+                axios[method](uri, form)
+                    .then(response => {
+                        this.getClients();
+
+                        form.name = '';
+                        form.redirect = '';
+                        form.errors = [];
+
+                        $(modal).modal('hide');
+                    })
+                    .catch(error => {
+                        if (typeof error.response.data === 'object') {
+                            form.errors = _.flatten(_.toArray(error.response.data.errors));
+                        } else {
+                            form.errors = ['Something went wrong. Please try again.'];
+                        }
+                    });
+            },
+
+            /**
+             * Destroy the given client.
+             */
+            destroy(client) {
+                axios.delete('/oauth/clients/' + client.id)
+                        .then(response => {
+                            this.getClients();
+                        });
+            }
+        }
+    }
+</script>

+ 298 - 0
resources/assets/js/components/passport/PersonalAccessTokens.vue

@@ -0,0 +1,298 @@
+<style scoped>
+    .action-link {
+        cursor: pointer;
+    }
+</style>
+
+<template>
+    <div>
+        <div>
+            <div class="card card-default mb-4">
+                <div class="card-header font-weight-bold bg-white">
+                    <div style="display: flex; justify-content: space-between; align-items: center;">
+                        <span>
+                            Personal Access Tokens
+                        </span>
+
+                        <a class="action-link" tabindex="-1" @click="showCreateTokenForm">
+                            Create New Token
+                        </a>
+                    </div>
+                </div>
+
+                <div class="card-body">
+                    <!-- No Tokens Notice -->
+                    <p class="mb-0" v-if="tokens.length === 0">
+                        You have not created any personal access tokens.
+                    </p>
+
+                    <!-- Personal Access Tokens -->
+                    <table class="table table-borderless mb-0" v-if="tokens.length > 0">
+                        <thead>
+                            <tr>
+                                <th>Name</th>
+                                <th></th>
+                            </tr>
+                        </thead>
+
+                        <tbody>
+                            <tr v-for="token in tokens">
+                                <!-- Client Name -->
+                                <td style="vertical-align: middle;">
+                                    {{ token.name }}
+                                </td>
+
+                                <!-- Delete Button -->
+                                <td style="vertical-align: middle;">
+                                    <a class="action-link text-danger" @click="revoke(token)">
+                                        Delete
+                                    </a>
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+
+        <!-- Create Token Modal -->
+        <div class="modal fade" id="modal-create-token" tabindex="-1" role="dialog">
+            <div class="modal-dialog">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h4 class="modal-title">
+                            Create Token
+                        </h4>
+
+                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    </div>
+
+                    <div class="modal-body">
+                        <!-- Form Errors -->
+                        <div class="alert alert-danger" v-if="form.errors.length > 0">
+                            <p class="mb-0"><strong>Whoops!</strong> Something went wrong!</p>
+                            <br>
+                            <ul>
+                                <li v-for="error in form.errors">
+                                    {{ error }}
+                                </li>
+                            </ul>
+                        </div>
+
+                        <!-- Create Token Form -->
+                        <form role="form" @submit.prevent="store">
+                            <!-- Name -->
+                            <div class="form-group row">
+                                <label class="col-md-4 col-form-label">Name</label>
+
+                                <div class="col-md-6">
+                                    <input id="create-token-name" type="text" class="form-control" name="name" v-model="form.name" autocomplete="off">
+                                </div>
+                            </div>
+
+                            <!-- Scopes -->
+                            <div class="form-group row" v-if="scopes.length > 0">
+                                <label class="col-md-4 col-form-label">Scopes</label>
+
+                                <div class="col-md-6">
+                                    <div v-for="scope in scopes">
+                                        <div class="checkbox">
+                                            <label>
+                                                <input type="checkbox"
+                                                    @click="toggleScope(scope.id)"
+                                                    :checked="scopeIsAssigned(scope.id)">
+
+                                                    {{ scope.id }}
+                                            </label>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+
+                    <!-- Modal Actions -->
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-secondary font-weight-bold" data-dismiss="modal">Close</button>
+
+                        <button type="button" class="btn btn-primary font-weight-bold" @click="store">
+                            Create
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- Access Token Modal -->
+        <div class="modal fade" id="modal-access-token" tabindex="-1" role="dialog">
+            <div class="modal-dialog">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h4 class="modal-title">
+                            Personal Access Token
+                        </h4>
+
+                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    </div>
+
+                    <div class="modal-body">
+                        <p>
+                            Here is your new personal access token. This is the only time it will be shown so don't lose it!
+                            You may now use this token to make API requests.
+                        </p>
+
+                        <textarea class="form-control" rows="10">{{ accessToken }}</textarea>
+                    </div>
+
+                    <!-- Modal Actions -->
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        /*
+         * The component's data.
+         */
+        data() {
+            return {
+                accessToken: null,
+
+                tokens: [],
+                scopes: [],
+
+                form: {
+                    name: '',
+                    scopes: [],
+                    errors: []
+                }
+            };
+        },
+
+        /**
+         * Prepare the component (Vue 1.x).
+         */
+        ready() {
+            this.prepareComponent();
+        },
+
+        /**
+         * Prepare the component (Vue 2.x).
+         */
+        mounted() {
+            this.prepareComponent();
+        },
+
+        methods: {
+            /**
+             * Prepare the component.
+             */
+            prepareComponent() {
+                this.getTokens();
+                this.getScopes();
+
+                $('#modal-create-token').on('shown.bs.modal', () => {
+                    $('#create-token-name').focus();
+                });
+            },
+
+            /**
+             * Get all of the personal access tokens for the user.
+             */
+            getTokens() {
+                axios.get('/oauth/personal-access-tokens')
+                        .then(response => {
+                            this.tokens = response.data;
+                        });
+            },
+
+            /**
+             * Get all of the available scopes.
+             */
+            getScopes() {
+                axios.get('/oauth/scopes')
+                        .then(response => {
+                            this.scopes = response.data;
+                        });
+            },
+
+            /**
+             * Show the form for creating new tokens.
+             */
+            showCreateTokenForm() {
+                $('#modal-create-token').modal('show');
+            },
+
+            /**
+             * Create a new personal access token.
+             */
+            store() {
+                this.accessToken = null;
+
+                this.form.errors = [];
+
+                axios.post('/oauth/personal-access-tokens', this.form)
+                        .then(response => {
+                            this.form.name = '';
+                            this.form.scopes = [];
+                            this.form.errors = [];
+
+                            this.tokens.push(response.data.token);
+
+                            this.showAccessToken(response.data.accessToken);
+                        })
+                        .catch(error => {
+                            if (typeof error.response.data === 'object') {
+                                this.form.errors = _.flatten(_.toArray(error.response.data.errors));
+                            } else {
+                                this.form.errors = ['Something went wrong. Please try again.'];
+                            }
+                        });
+            },
+
+            /**
+             * Toggle the given scope in the list of assigned scopes.
+             */
+            toggleScope(scope) {
+                if (this.scopeIsAssigned(scope)) {
+                    this.form.scopes = _.reject(this.form.scopes, s => s == scope);
+                } else {
+                    this.form.scopes.push(scope);
+                }
+            },
+
+            /**
+             * Determine if the given scope has been assigned to the token.
+             */
+            scopeIsAssigned(scope) {
+                return _.indexOf(this.form.scopes, scope) >= 0;
+            },
+
+            /**
+             * Show the given access token to the user.
+             */
+            showAccessToken(accessToken) {
+                $('#modal-create-token').modal('hide');
+
+                this.accessToken = accessToken;
+
+                $('#modal-access-token').modal('show');
+            },
+
+            /**
+             * Revoke the given token.
+             */
+            revoke(token) {
+                axios.delete('/oauth/personal-access-tokens/' + token.id)
+                        .then(response => {
+                            this.getTokens();
+                        });
+            }
+        }
+    }
+</script>