- added working typescript support to vue
- added bouncer support - added UserPolicies - added first user index call
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
"@adonisjs/core/build/commands/index.js",
|
||||
"@adonisjs/repl/build/commands",
|
||||
"@eidellev/inertia-adonisjs/build/commands",
|
||||
"@adonisjs/lucid/build/commands"
|
||||
"@adonisjs/lucid/build/commands",
|
||||
"@adonisjs/bouncer/build/commands"
|
||||
],
|
||||
"exceptionHandlerNamespace": "App/Exceptions/Handler",
|
||||
"aliases": {
|
||||
@@ -22,7 +23,8 @@
|
||||
"environment": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"./start/bouncer"
|
||||
],
|
||||
"providers": [
|
||||
"./providers/AppProvider",
|
||||
@@ -32,7 +34,8 @@
|
||||
"@adonisjs/shield",
|
||||
"@eidellev/inertia-adonisjs",
|
||||
"@adonisjs/lucid",
|
||||
"@adonisjs/auth"
|
||||
"@adonisjs/auth",
|
||||
"@adonisjs/bouncer"
|
||||
],
|
||||
"metaFiles": [
|
||||
{
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
@@ -591,6 +591,42 @@
|
||||
"description": "Disable locks acquired to run migrations safely"
|
||||
}
|
||||
]
|
||||
},
|
||||
"make:policy": {
|
||||
"settings": {},
|
||||
"commandPath": "@adonisjs/bouncer/build/commands/MakePolicy",
|
||||
"commandName": "make:policy",
|
||||
"description": "Make a new bouncer policy",
|
||||
"args": [
|
||||
{
|
||||
"type": "string",
|
||||
"propertyName": "name",
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"description": "Name of the policy to create"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"flags": [
|
||||
{
|
||||
"name": "resource-model",
|
||||
"propertyName": "resourceModel",
|
||||
"type": "string",
|
||||
"description": "Name of the resource model to authorize"
|
||||
},
|
||||
{
|
||||
"name": "user-model",
|
||||
"propertyName": "userModel",
|
||||
"type": "string",
|
||||
"description": "Name of the user model to be authorized"
|
||||
},
|
||||
{
|
||||
"name": "actions",
|
||||
"propertyName": "actions",
|
||||
"type": "array",
|
||||
"description": "Actions to implement"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"aliases": {}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||
import { schema } from '@ioc:Adonis/Core/Validator'
|
||||
|
||||
export default class AuthController {
|
||||
public async login({ auth, request, response, session }: HttpContextContract){
|
||||
public async login({ auth, request, response }: HttpContextContract){
|
||||
|
||||
|
||||
const loginSchema = schema.create({
|
||||
@@ -18,27 +18,13 @@ export default class AuthController {
|
||||
}
|
||||
})
|
||||
|
||||
session.flash({
|
||||
login: {
|
||||
warning: 'test'
|
||||
}
|
||||
})
|
||||
|
||||
await auth.attempt(username, password)
|
||||
response.redirect().toRoute('events.index')
|
||||
}
|
||||
|
||||
public async logout({ auth, response, session }: HttpContextContract) {
|
||||
public async logout({ auth, response }: HttpContextContract) {
|
||||
await auth.logout()
|
||||
|
||||
session.flash('gfd', {
|
||||
warning: 'test'
|
||||
})
|
||||
session.flash('login', {
|
||||
warning: "noch eine warning"
|
||||
})
|
||||
|
||||
|
||||
response.redirect('/login')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||
import User from 'App/Models/User'
|
||||
import Logger from '@ioc:Adonis/Core/Logger'
|
||||
import Database from '@ioc:Adonis/Lucid/Database'
|
||||
|
||||
export default class UsersController {
|
||||
public async index({ auth, response, inertia }: HttpContextContract) {
|
||||
public async index({ inertia, bouncer }: HttpContextContract) {
|
||||
|
||||
if(auth.user?.isAdmin) {
|
||||
return inertia.render('Users/Index')
|
||||
}
|
||||
else response.redirect().toRoute('events.index')
|
||||
await bouncer.with('UserPolicy').authorize('index')
|
||||
|
||||
const users = await Database
|
||||
.from('users')
|
||||
.select('id', 'username', 'is_admin')
|
||||
|
||||
return inertia.render('Users/Index', { users })
|
||||
}
|
||||
|
||||
public async create({ auth, inertia }: HttpContextContract) {
|
||||
|
||||
@@ -38,6 +38,10 @@ export default class ExceptionHandler extends HttpExceptionHandler {
|
||||
session.flash('login', { error: error.message });
|
||||
return response.redirect().back();
|
||||
}
|
||||
if(['E_AUTHORIZATION_FAILURE'].includes(error.code)) {
|
||||
session.flash('auth', { error: error.message })
|
||||
return response.redirect().back()
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward rest of the exceptions to the parent class
|
||||
|
||||
@@ -12,7 +12,10 @@ export default class User extends BaseModel {
|
||||
@column({ serializeAs: null })
|
||||
public password: string
|
||||
|
||||
@column()
|
||||
@column({
|
||||
consume: Boolean,
|
||||
serialize: Boolean
|
||||
})
|
||||
public isAdmin: boolean
|
||||
|
||||
@column()
|
||||
|
||||
25
app/Policies/UserPolicy.ts
Normal file
25
app/Policies/UserPolicy.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import User from 'App/Models/User'
|
||||
import { BasePolicy } from '@ioc:Adonis/Addons/Bouncer'
|
||||
|
||||
export default class UserPolicy extends BasePolicy {
|
||||
public async index(user: User) {
|
||||
return user.isAdmin
|
||||
}
|
||||
|
||||
public async show(user: User, query: User) {
|
||||
return user.isAdmin || user.id === query.id
|
||||
}
|
||||
|
||||
public async update(user: User, query: User) {
|
||||
return user.isAdmin || user.id === query.id
|
||||
}
|
||||
|
||||
public async destroy(user: User) {
|
||||
return user.isAdmin
|
||||
}
|
||||
|
||||
public async store(user: User) {
|
||||
return user.isAdmin
|
||||
}
|
||||
|
||||
}
|
||||
16
contracts/bouncer.ts
Normal file
16
contracts/bouncer.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Contract source: https://git.io/Jte3v
|
||||
*
|
||||
* Feel free to let us know via PR, if you find something broken in this config
|
||||
* file.
|
||||
*/
|
||||
|
||||
import { actions, policies } from '../start/bouncer'
|
||||
|
||||
declare module '@ioc:Adonis/Addons/Bouncer' {
|
||||
type ApplicationActions = ExtractActionsTypes<typeof actions>
|
||||
type ApplicationPolicies = ExtractPoliciesTypes<typeof policies>
|
||||
|
||||
interface ActionsList extends ApplicationActions {}
|
||||
interface PoliciesList extends ApplicationPolicies {}
|
||||
}
|
||||
883
package-lock.json
generated
883
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@adonisjs/auth": "^8.2.3",
|
||||
"@adonisjs/bouncer": "^2.3.0",
|
||||
"@adonisjs/core": "^5.9.0",
|
||||
"@adonisjs/lucid": "^18.4.0",
|
||||
"@adonisjs/repl": "^3.1.11",
|
||||
@@ -76,11 +77,13 @@
|
||||
"@eidellev/inertia-adonisjs": "^8.0.1",
|
||||
"@inertiajs/vue3": "^1.0.9",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"luxon": "^3.3.0",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"source-map-support": "^0.5.21",
|
||||
"sqlite3": "^5.1.6",
|
||||
"ts-loader": "^9.4.4",
|
||||
"vue": "^3.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,6 @@
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { watch, defineProps, onUpdated, onMounted } from 'vue'
|
||||
import { onUpdated, onMounted } from 'vue'
|
||||
|
||||
const message = useMessage()
|
||||
const props = defineProps(['messages'])
|
||||
@@ -15,16 +15,13 @@
|
||||
displayNewMessages(props.messages)
|
||||
})
|
||||
|
||||
function displayNewMessages(messages) {
|
||||
function displayNewMessages(messages: any) {
|
||||
|
||||
console.log(messages)
|
||||
let output = []
|
||||
let output: Array<Object> = []
|
||||
|
||||
output = flattenObject(removeValidationErrors(messages))
|
||||
|
||||
console.log(output)
|
||||
|
||||
output?.forEach((item) => {
|
||||
output?.forEach((item: any) => {
|
||||
for (let key in item) {
|
||||
switch (key){
|
||||
case 'error':
|
||||
@@ -55,33 +52,32 @@
|
||||
})
|
||||
}
|
||||
|
||||
function removeValidationErrors(input) {
|
||||
if(input === null || !Object.hasOwn(input, "errors")) return input
|
||||
function removeValidationErrors(input: any) {
|
||||
|
||||
if(input === null || !input.hasOwnProperty("errors")) return input
|
||||
|
||||
const { errors: _, ...output } = input
|
||||
const { errors: _, ...output } = (input as any)
|
||||
return output
|
||||
}
|
||||
|
||||
function flattenObject(input) {
|
||||
function flattenObject(input: Object) {
|
||||
if (input === null) return input
|
||||
|
||||
return Object.values(input).map((value) => Object.entries(value)).flat().reduce((acc, [key, value]) => {
|
||||
return Object.values(input).map((value) => Object.entries(value)).flat().reduce((acc: Array<Object>, [key, value]) => {
|
||||
acc.push({[key]: value});
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function translateError(errorMsg) {
|
||||
function translateError(errorMsg: string) {
|
||||
switch(errorMsg.split(":")[0]) {
|
||||
case 'E_INVALID_AUTH_PASSWORD':
|
||||
return "Falsches Passwort eingegeben."
|
||||
case 'E_INVALID_AUTH_UID':
|
||||
return "Benutzername nicht gefunden"
|
||||
case 'E_AUTHORIZATION_FAILURE':
|
||||
return 'Rechte unzureichend um diese Aktion auszuführen.'
|
||||
}
|
||||
return errorMsg
|
||||
}
|
||||
|
||||
function isEmpty(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
</script>
|
||||
@@ -1,15 +1,25 @@
|
||||
<template>
|
||||
<n-message-provider>
|
||||
<FlashMessages
|
||||
:messages="props.flashMessages"
|
||||
/>
|
||||
</n-message-provider>
|
||||
<div>
|
||||
Bin in Events
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import BELayout from '@/layouts/BELayout.vue'
|
||||
import FlashMessages from '@/components/FlashMessages.vue'
|
||||
|
||||
defineOptions({
|
||||
layout: BELayout
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
flashMessages: Object
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
<template>
|
||||
<n-message-provider>
|
||||
<FlashMessages
|
||||
:messages="props.flashMessages"
|
||||
/>
|
||||
</n-message-provider>
|
||||
<div>
|
||||
Willkommen auf der Hauptseite
|
||||
</div>
|
||||
<div>
|
||||
{{ helloWorld }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import FlashMessages from '@/components/FlashMessages.vue'
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
test: String,
|
||||
flashMessages: Object
|
||||
})
|
||||
|
||||
const helloWorld = ref<String>("helloWorld")
|
||||
|
||||
</script>
|
||||
@@ -46,11 +46,12 @@
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { router } from '@inertiajs/vue3'
|
||||
import type { FormInst } from 'naive-ui'
|
||||
import LoginLayout from '@/layouts/LoginLayout.vue'
|
||||
import FlashMessages from '@/components/FlashMessages'
|
||||
import FlashMessages from '@/components/FlashMessages.vue'
|
||||
|
||||
defineOptions({ layout: LoginLayout })
|
||||
const props = defineProps(['flashMessages'])
|
||||
@@ -60,7 +61,7 @@
|
||||
password: '',
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
|
||||
const rules = ref({
|
||||
username: {
|
||||
@@ -75,15 +76,6 @@
|
||||
}
|
||||
})
|
||||
|
||||
function translateLoginError(errorMsg) {
|
||||
switch(errorMsg.split(":")[0]) {
|
||||
case 'E_INVALID_AUTH_PASSWORD':
|
||||
return "Falsches Passwort eingegeben."
|
||||
case 'E_INVALID_AUTH_UID':
|
||||
return "Benutzername nicht gefunden"
|
||||
}
|
||||
}
|
||||
|
||||
function onClickLogin(){
|
||||
formRef.value?.validate((errors) => {
|
||||
if(!errors) router.post('login', form.value)
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
<template>
|
||||
<n-message-provider>
|
||||
<FlashMessages
|
||||
:messages="props.flashMessages"
|
||||
/>
|
||||
</n-message-provider>
|
||||
<div>
|
||||
Bin in Users
|
||||
</div>
|
||||
<div>
|
||||
{{ users }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
|
||||
import BELayout from '@/layouts/BELayout.vue'
|
||||
import MainNav from '@/components/MainNav.vue'
|
||||
import FlashMessages from '@/components/FlashMessages.vue'
|
||||
|
||||
defineOptions({ layout: BELayout })
|
||||
|
||||
const props = defineProps({
|
||||
users: Object,
|
||||
flashMessages: Object
|
||||
})
|
||||
|
||||
</script>
|
||||
5
resources/js/vue-shim.d.ts
vendored
Normal file
5
resources/js/vue-shim.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from "vue"
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
59
start/bouncer.ts
Normal file
59
start/bouncer.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Contract source: https://git.io/Jte3T
|
||||
*
|
||||
* Feel free to let us know via PR, if you find something broken in this config
|
||||
* file.
|
||||
*/
|
||||
|
||||
import Bouncer from '@ioc:Adonis/Addons/Bouncer'
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bouncer Actions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Actions allows you to separate your application business logic from the
|
||||
| authorization logic. Feel free to make use of policies when you find
|
||||
| yourself creating too many actions
|
||||
|
|
||||
| You can define an action using the `.define` method on the Bouncer object
|
||||
| as shown in the following example
|
||||
|
|
||||
| ```
|
||||
| Bouncer.define('deletePost', (user: User, post: Post) => {
|
||||
| return post.user_id === user.id
|
||||
| })
|
||||
| ```
|
||||
|
|
||||
|****************************************************************
|
||||
| NOTE: Always export the "actions" const from this file
|
||||
|****************************************************************
|
||||
*/
|
||||
export const { actions } = Bouncer
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bouncer Policies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Policies are self contained actions for a given resource. For example: You
|
||||
| can create a policy for a "User" resource, one policy for a "Post" resource
|
||||
| and so on.
|
||||
|
|
||||
| The "registerPolicies" accepts a unique policy name and a function to lazy
|
||||
| import the policy
|
||||
|
|
||||
| ```
|
||||
| Bouncer.registerPolicies({
|
||||
| UserPolicy: () => import('App/Policies/User'),
|
||||
| PostPolicy: () => import('App/Policies/Post')
|
||||
| })
|
||||
| ```
|
||||
|
|
||||
|****************************************************************
|
||||
| NOTE: Always export the "policies" const from this file
|
||||
|****************************************************************
|
||||
*/
|
||||
export const { policies } = Bouncer.registerPolicies({
|
||||
UserPolicy: () => import('App/Policies/UserPolicy')
|
||||
})
|
||||
@@ -8,7 +8,6 @@
|
||||
"build"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "build",
|
||||
"rootDir": "./",
|
||||
"sourceMap": true,
|
||||
@@ -35,7 +34,8 @@
|
||||
"@japa/preset-adonis/build/adonis-typings",
|
||||
"@eidellev/inertia-adonisjs",
|
||||
"@adonisjs/lucid",
|
||||
"@adonisjs/auth"
|
||||
"@adonisjs/auth",
|
||||
"@adonisjs/bouncer"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
13
tsconfig.vue.json
Normal file
13
tsconfig.vue.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// tsconfig.vue.json
|
||||
|
||||
"extends": "@vue/tsconfig/tsconfig.json",
|
||||
"include": [
|
||||
"./resources/js/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./resources/js/"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ Encore.setPublicPath('/assets')
|
||||
| entrypoints.
|
||||
|
|
||||
*/
|
||||
Encore.addEntry('app', './resources/js/app.js')
|
||||
Encore.addEntry('app', './resources/js/app.ts')
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -187,6 +187,8 @@ Encore.enableVueLoader(() => {}, {
|
||||
version: 3,
|
||||
runtimeCompilerBuild: false,
|
||||
useJsx: false
|
||||
}).enableTypeScriptLoader(config => {
|
||||
config.configFile = 'tsconfig.vue.json'
|
||||
}).addAliases({
|
||||
'@': join(__dirname, 'resources/js')
|
||||
}).configureDefinePlugin(options => {
|
||||
|
||||
Reference in New Issue
Block a user