[admin] agent allow&deny

This commit is contained in:
xiaomlove
2022-02-25 18:09:31 +08:00
parent e2f30ecf0c
commit 9edbaf49ca
25 changed files with 1420 additions and 28 deletions

View File

@@ -26,12 +26,20 @@
</el-menu-item-group>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
<span>Agent</span>
</template>
<el-menu-item-group>
<el-menu-item index="/agent-allow"><i class="el-icon-user" />Allow</el-menu-item>
</el-menu-item-group>
<el-menu-item-group>
<el-menu-item index="/agent-deny"><i class="el-icon-user" />Deny</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
<el-sub-menu index="4">
<template #title>
<span>System</span>
</template>
<!-- <el-menu-item-group>-->
<!-- <el-menu-item index="/agent-allow"><i class="el-icon-menu" />Agent allow</el-menu-item>-->
<!-- </el-menu-item-group>-->
<el-menu-item-group>
<el-menu-item index="/exam"><i class="el-icon-menu" />Exam</el-menu-item>
</el-menu-item-group>

View File

@@ -64,6 +64,26 @@ const router = createRouter({
name: 'medal-form',
component: () => import('../views/medal/form.vue')
},
{
path: '/agent-allow',
name: 'agent-allow',
component: () => import('../views/agent-allow/index.vue')
},
{
path: '/agent-allow-form',
name: 'agent-allow-form',
component: () => import('../views/agent-allow/form.vue')
},
{
path: '/agent-deny',
name: 'agent-deny',
component: () => import('../views/agent-deny/index.vue')
},
{
path: '/agent-deny-form',
name: 'agent-deny-form',
component: () => import('../views/agent-deny/form.vue')
},
{
path: '/setting',
name: 'setting',

View File

@@ -1,6 +1,7 @@
.nexus-table-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.nexus-help-text {
color: #aaa;

View File

@@ -7,22 +7,44 @@ const api = {
logout: (params = {}) => {
return axios.post('logout');
},
listAllowAgent: (params = {}) => {
listAgentAllow: (params = {}) => {
return axios.get('agent-allows', {params: params});
},
storeAllowAgent: (params = {}) => {
listAllAgentAllow: (params = {}) => {
return axios.get('all-agent-allows', {params: params});
},
storeAgentAllow: (params = {}) => {
return axios.post('agent-allows', params);
},
updateAllowAgent: (id, params = {}) => {
updateAgentAllow: (id, params = {}) => {
return axios.put('agent-allows/' + id, params);
},
getAllowAgent: (id) => {
getAgentAllow: (id) => {
return axios.get('agent-allows/' + id);
},
deleteAllowAgent: (id) => {
deleteAgentAllow: (id) => {
return axios.delete('agent-allows/' + id);
},
listAgentDeny: (params = {}) => {
return axios.get('agent-denies', {params: params});
},
storeAgentDeny: (params = {}) => {
return axios.post('agent-denies', params);
},
updateAgentDeny: (id, params = {}) => {
return axios.put('agent-denies/' + id, params);
},
getAgentDeny: (id) => {
return axios.get('agent-denies/' + id);
},
deleteAgentDeny: (id) => {
return axios.delete('agent-denies/' + id);
},
checkAgent: (params = {}) => {
return axios.post('agent-check', params);
},
listUser: (params = {}) => {
return axios.get('users', {params: params});
},

View File

@@ -40,6 +40,8 @@ export const pathMap = {
account: '修改账户',
'agent-allow': 'Agent allow',
'agent-allow-form': 'Agent allow form',
'agent-deny': 'Agent deny',
'agent-deny-form': 'Agent deny form',
'user': 'User',
'user-form': 'User form',
'user-detail': 'User detail',

View File

@@ -6,7 +6,7 @@ const useTable = () => {
query: {
page: 1,
sort_field: 'id',
sort_type: 'desc'
sort_type: 'desc',
},
tableData: [],
multipleSelection: [],

View File

@@ -0,0 +1,78 @@
<template>
<el-dialog
title="Check client is allowed or not"
v-model="visible"
center
:close-on-click-modal="false"
>
<el-form
:model="formData"
label-width="100px"
v-loading="loading"
ref="formRef"
:rules="rules">
<el-form-item label="Peer id" prop="peer_id">
<el-input type="text" v-model="formData.peer_id"></el-input>
</el-form-item>
<el-form-item label="Agent" prop="agent">
<el-input type="text" v-model="formData.agent"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">Cancel</el-button>
<el-button type="primary" @click="handleSubmit">Check</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import { onMounted, reactive, ref, toRefs } from 'vue'
import { ElMessage } from 'element-plus'
import {useRoute, useRouter} from 'vue-router'
import api from '../../utils/api'
export default {
name: "DialogCheck",
props: {
reload: Function
},
setup(props, context) {
const formRef = ref(null)
const state = reactive({
loading: false,
visible: false,
result: '',
formData: {
peer_id: '',
agent: '',
},
rules: {
peer_id: [{ required: 'true'}],
agent: [{ required: 'true'}]
}
})
const open = (uid) => {
state.formData.uid = uid
state.visible = true
}
const handleSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res = await api.checkAgent(state.formData)
ElMessage.success(res.msg)
}
})
}
return {
...toRefs(state),
handleSubmit,
formRef,
open,
}
}
}
</script>

View File

@@ -1,3 +1,199 @@
<template>
<div>Agent Allow Form</div>
<div>
<el-row>
<el-col :span="12">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="200px" class="formData">
<el-form-item label="Family" prop="family">
<el-input v-model="formData.family" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Start name" prop="start_name">
<el-input v-model="formData.start_name" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Peer id start" prop="peer_id_start">
<el-input v-model="formData.peer_id_start" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Peer id pattern" prop="peer_id_pattern">
<el-input v-model="formData.peer_id_pattern" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Peer id match num" prop="peer_id_match_num">
<el-input v-model="formData.peer_id_match_num" placeholder="" type="number"></el-input>
</el-form-item>
<el-form-item label="Peer id match type" prop="peer_id_matchtype">
<el-radio-group v-model="formData.peer_id_matchtype">
<el-radio label="dec">dec</el-radio>
<el-radio label="hex">hex</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Agent start" prop="agent_start">
<el-input v-model="formData.agent_start" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Agent pattern" prop="agent_pattern">
<el-input v-model="formData.agent_pattern" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Agent match num" prop="agent_match_num">
<el-input v-model="formData.agent_match_num" placeholder="" type="number"></el-input>
</el-form-item>
<el-form-item label="Agent match type" prop="agent_matchtype">
<el-radio-group v-model="formData.agent_matchtype">
<el-radio label="dec">dec</el-radio>
<el-radio label="hex">hex</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Exception" prop="exception">
<el-radio-group v-model="formData.exception">
<el-radio label="yes">Yes</el-radio>
<el-radio label="no">No</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Allow https" prop="allowhttps">
<el-radio-group v-model="formData.allowhttps">
<el-radio label="yes">Yes</el-radio>
<el-radio label="no">No</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Comment" prop="comment">
<el-input type="textarea" v-model="formData.comment" placeholder=""></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAdd()">Submit</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import { reactive, ref, toRefs, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { localGet } from '../../utils'
import api from "../../utils/api";
export default {
name: 'AgentAllowForm',
setup() {
const { proxy } = getCurrentInstance()
console.log('proxy', proxy)
const formRef = ref(null)
const route = useRoute()
const router = useRouter()
const { id } = route.query
const state = reactive({
token: localGet('token') || '',
id: id,
allClasses: [],
formData: {
family: '',
start_name: '',
peer_id_pattern: '',
peer_id_match_num: '',
peer_id_matchtype: '',
peer_id_start: '',
agent_pattern: '',
agent_match_num: '',
agent_matchtype: '',
agent_start: '',
exception: '',
allowhttps: '',
comment: '',
},
rules: {
family: [
{ required: 'true', }
],
start_name: [
{ required: 'true', }
],
peer_id_pattern: [
{ required: 'true', }
],
peer_id_match_num: [
{ required: 'true', }
],
peer_id_matchtype: [
{ required: 'true', }
],
peer_id_start: [
{required: 'true'}
],
agent_pattern: [
{required: 'true'}
],
agent_match_num: [
{required: 'true'}
],
agent_matchtype: [
{required: 'true'}
],
agent_start: [
{required: 'true'}
],
exception: [
{required: 'true'}
],
allowhttps: [
{required: 'true'}
],
},
})
onMounted( async () => {
if (id) {
api.getAgentAllow(id).then(res => {
state.formData.family = res.data.family
state.formData.start_name = res.data.start_name
state.formData.peer_id_pattern = res.data.peer_id_pattern
state.formData.peer_id_match_num = res.data.peer_id_match_num
state.formData.peer_id_matchtype = res.data.peer_id_matchtype
state.formData.peer_id_start = res.data.peer_id_start
state.formData.agent_pattern = res.data.agent_pattern
state.formData.agent_match_num = res.data.agent_match_num
state.formData.agent_matchtype = res.data.agent_matchtype
state.formData.agent_start = res.data.agent_start
state.formData.exception = res.data.exception
state.formData.allowhttps = res.data.allowhttps
state.formData.comment = res.data.comment
})
}
})
onBeforeUnmount(() => {
})
const submitAdd = () => {
formRef.value.validate(async (vaild) => {
if (vaild) {
let params = state.formData;
console.log(params)
if (id) {
await api.updateAgentAllow(id, params)
} else {
await api.storeAgentAllow(params)
}
await router.push({name: 'agent-allow'})
}
})
}
const getAgentAllow = async (id) => {
let res = await api.getAgentAllow(id)
console.log(res)
}
return {
...toRefs(state),
formRef,
submitAdd,
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,3 +1,198 @@
<template>
<div>Agent Allow Index</div>
<el-card class="">
<template #header>
<div class="nexus-table-header">
<div class="left">
<el-form :inline="true" :model="query">
<el-form-item label="">
<el-input placeholder="Family" v-model="query.family"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleReset">Reset</el-button>
<el-button type="primary" @click="fetchTableData">Query</el-button>
</el-form-item>
</el-form>
</div>
<div class="right">
<el-button type="primary" icon="Check" @click="handleCheck">Check</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd">Add</el-button>
</div>
</div>
</template>
<el-table
v-loading="loading"
ref="multipleTable"
:data="tableData"
tooltip-effect="dark"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="Id"
width="50"
>
</el-table-column>
<el-table-column
prop="family"
label="Family"
></el-table-column>
<el-table-column
prop="start_name"
label="Start name"
>
</el-table-column>
<el-table-column
prop="peer_id_start"
label="Peer id start"
>
</el-table-column>
<el-table-column
prop="peer_id_pattern"
label="Peer id pattern"
></el-table-column>
<!-- <el-table-column-->
<!-- prop="peer_id_match_num"-->
<!-- label="Peer id match num"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="peer_id_matchtype"-->
<!-- label="Peer id match type"-->
<!-- ></el-table-column>-->
<el-table-column
prop="agent_start"
label="Agent start"
>
</el-table-column>
<el-table-column
prop="agent_pattern"
label="Agent pattern"
></el-table-column>
<!-- <el-table-column-->
<!-- prop="agent_match_num"-->
<!-- label="Agent match num"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="agent_matchtype"-->
<!-- label="Agent match type"-->
<!-- ></el-table-column>-->
<el-table-column
label="Action"
width="120"
>
<template #default="scope">
<a style="cursor: pointer; margin-right: 10px" @click="handleEdit(scope.row.id)">Edit</a>
<el-popconfirm
title="Confirm Delete ?"
@confirm="handleDelete(scope.row.id)"
>
<template #reference>
<a style="cursor: pointer">Delete</a>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!--总数超过一页再展示分页器-->
<el-pagination
background
layout="prev, pager, next"
:total="total"
:page-size="perPage"
:current-page="currentPage"
@current-change="changePage"
/>
</el-card>
<DialogCheck ref="refDialogCheck" />
</template>
<script>
import { onMounted, reactive, ref, toRefs } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import api from '../../utils/api'
import { useTable, renderTableData } from '../../utils/table'
import DialogCheck from "../agent-allow/dialog-check.vue"
export default {
name: 'ClientTable',
components: {
DialogCheck,
},
setup() {
const multipleTable = ref(null)
const router = useRouter()
const state = useTable()
const refDialogCheck = ref(null)
onMounted(() => {
console.log('MedalTable onMounted')
fetchTableData()
})
const fetchTableData = async () => {
state.loading = true
let res = await api.listAgentAllow(state.query)
renderTableData(res, state)
state.loading = false
}
const handleAdd = () => {
router.push({ name: 'agent-allow-form' })
}
const handleEdit = (id) => {
router.push({ path: '/agent-allow-form', query: { id } })
}
const handleDelete = async (id) => {
let res = await api.deleteAgentAllow(id)
ElMessage.success(res.msg)
state.query.page = 1;
await fetchTableData()
}
const handleSelectionChange = (val) => {
state.multipleSelection = val
}
const changePage = (val) => {
state.query.page = val
fetchTableData()
}
const handleReset = () => {
state.query.family = '';
}
const handleCheck = () => {
refDialogCheck.value.open()
}
return {
...toRefs(state),
multipleTable,
handleSelectionChange,
handleAdd,
handleEdit,
handleDelete,
fetchTableData,
changePage,
handleReset,
handleCheck,
refDialogCheck
}
}
}
</script>
<style scoped>
.swiper-container {
min-height: 100%;
}
.el-card.is-always-shadow {
min-height: 100%!important;
}
</style>

View File

@@ -0,0 +1,133 @@
<template>
<div>
<el-row>
<el-col :span="12">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="200px" class="formData">
<el-form-item label="Family" prop="family_id">
<el-select v-model="formData.family_id" filterable>
<el-option
v-for="item in agentAllows"
:key="item.id"
:label="item.family"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Name" prop="name">
<el-input v-model="formData.name" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Peer id" prop="peer_id">
<el-input v-model="formData.peer_id" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Agent" prop="agent">
<el-input v-model="formData.agent" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Comment" prop="comment">
<el-input type="textarea" v-model="formData.comment" placeholder=""></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAdd()">Submit</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import { reactive, ref, toRefs, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { localGet } from '../../utils'
import api from "../../utils/api";
export default {
name: 'AgentAllowForm',
setup() {
const { proxy } = getCurrentInstance()
console.log('proxy', proxy)
const formRef = ref(null)
const route = useRoute()
const router = useRouter()
const { id } = route.query
const state = reactive({
token: localGet('token') || '',
id: id,
agentAllows: [],
formData: {
family_id: '',
name: '',
peer_id: '',
agent: '',
comment: '',
},
rules: {
family_id: [
{ required: 'true', }
],
name: [
{ required: 'true', }
],
peer_id: [
{ required: 'true', }
],
agent: [
{ required: 'true', }
],
},
})
onMounted( async () => {
await listAgentAllows()
if (id) {
api.getAgentDeny(id).then(res => {
state.formData.family_id = res.data.family_id
state.formData.name = res.data.name
state.formData.peer_id = res.data.peer_id
state.formData.agent = res.data.agent
state.formData.comment = res.data.comment
})
}
})
onBeforeUnmount(() => {
})
const submitAdd = () => {
formRef.value.validate(async (vaild) => {
if (vaild) {
let params = state.formData;
console.log(params)
if (id) {
await api.updateAgentDeny(id, params)
} else {
await api.storeAgentDeny(params)
}
await router.push({name: 'agent-deny'})
}
})
}
const listAgentAllows = async () => {
let res = await api.listAllAgentAllow()
state.agentAllows = res.data
}
const getAgentAllow = async (id) => {
let res = await api.getAgentAllow(id)
console.log(res)
}
return {
...toRefs(state),
formRef,
submitAdd,
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,181 @@
<template>
<el-card class="">
<template #header>
<div class="nexus-table-header">
<div class="left">
<el-form :inline="true" :model="query">
<el-form-item label="">
<el-select v-model="query.family_id" filterable placeholder="Family">
<el-option
v-for="item in extraData.agentAllows"
:key="item.id"
:label="item.family"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleReset">Reset</el-button>
<el-button type="primary" @click="fetchTableData">Query</el-button>
</el-form-item>
</el-form>
</div>
<div class="right">
<el-button type="primary" icon="Plus" @click="handleAdd">Add</el-button>
</div>
</div>
</template>
<el-table
v-loading="loading"
ref="multipleTable"
:data="tableData"
tooltip-effect="dark"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="Id"
width="50"
>
</el-table-column>
<el-table-column
prop="family_id"
label="Family"
:formatter="formatColumnFamilyId"
></el-table-column>
<el-table-column
prop="name"
label="Name"
>
</el-table-column>
<el-table-column
prop="peer_id"
label="Peer id"
>
</el-table-column>
<el-table-column
prop="agent"
label="Agent"
></el-table-column>
<el-table-column
label="Action"
width="120"
>
<template #default="scope">
<a style="cursor: pointer; margin-right: 10px" @click="handleEdit(scope.row.id)">Edit</a>
<el-popconfirm
title="Confirm Delete ?"
@confirm="handleDelete(scope.row.id)"
>
<template #reference>
<a style="cursor: pointer">Delete</a>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!--总数超过一页再展示分页器-->
<el-pagination
background
layout="prev, pager, next"
:total="total"
:page-size="perPage"
:current-page="currentPage"
@current-change="changePage"
/>
</el-card>
</template>
<script>
import { onMounted, reactive, ref, toRefs } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import api from '../../utils/api'
import { useTable, renderTableData } from '../../utils/table'
export default {
name: 'ClientTable',
setup() {
const multipleTable = ref(null)
const router = useRouter()
const state = useTable()
let extraData = reactive({
agentAllows: []
});
onMounted(() => {
console.log('MedalTable onMounted')
fetchTableData()
})
const fetchTableData = async () => {
state.loading = true
await listAgentAllows()
let res = await api.listAgentDeny(state.query)
renderTableData(res, state)
state.loading = false
}
const handleAdd = () => {
router.push({ name: 'agent-deny-form' })
}
const handleEdit = (id) => {
router.push({ path: '/agent-deny-form', query: { id } })
}
const handleDelete = async (id) => {
let res = await api.deleteAgentDeny(id)
ElMessage.success(res.msg)
state.query.page = 1;
await fetchTableData()
}
const handleSelectionChange = (val) => {
state.multipleSelection = val
}
const changePage = (val) => {
state.query.page = val
fetchTableData()
}
const listAgentAllows = async () => {
let res = await api.listAllAgentAllow()
extraData.agentAllows = res.data
}
const handleReset = () => {
state.query.family_id = '';
}
const formatColumnFamilyId = (row, column) => {
return row.family.family
}
return {
...toRefs(state),
extraData,
multipleTable,
handleSelectionChange,
handleAdd,
handleEdit,
handleDelete,
fetchTableData,
changePage,
handleReset,
formatColumnFamilyId
}
}
}
</script>
<style scoped>
.swiper-container {
min-height: 100%;
}
.el-card.is-always-shadow {
min-height: 100%!important;
}
</style>

View File

@@ -3,10 +3,24 @@
<template #header>
<div class="nexus-table-header">
<div class="left">
<el-form :inline="true" :model="query">
<el-form-item label="">
<el-input placeholder="ID" v-model="query.id"></el-input>
</el-form-item>
<el-form-item label="">
<el-input placeholder="Username" v-model="query.username"></el-input>
</el-form-item>
<el-form-item label="">
<el-input placeholder="Email" v-model="query.email"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleReset">Reset</el-button>
<el-button type="primary" @click="fetchTableData">Query</el-button>
</el-form-item>
</el-form>
</div>
<div class="right">
<el-button type="primary" size="small" icon="Plus" @click="handleAdd">Add</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd">Add</el-button>
</div>
</div>
</template>
@@ -26,7 +40,7 @@
<el-table-column
prop="id"
label="Id"
width="60"
width="80"
sortable="custom"
></el-table-column>
@@ -157,6 +171,12 @@ export default {
fetchTableData()
}
const handleReset = () => {
state.query.id = '';
state.query.username = '';
state.query.email = '';
}
const handleDetail = (id) => {
router.push({
name: 'user-detail',
@@ -187,7 +207,8 @@ export default {
handleSortChange,
formatColumnClass,
formatColumnUploaded,
formatColumnDownloaded
formatColumnDownloaded,
handleReset
}
}
}

View File

@@ -11,6 +11,7 @@ use App\Models\Medal;
use App\Models\SearchBox;
use App\Models\Snatch;
use App\Models\User;
use App\Repositories\AgentAllowRepository;
use App\Repositories\ExamRepository;
use App\Repositories\HitAndRunRepository;
use App\Repositories\SearchBoxRepository;
@@ -58,8 +59,10 @@ class Test extends Command
*/
public function handle()
{
$rep = new HitAndRunRepository();
$r = $rep->cronjobUpdateStatus();
$peerId = '-TR2920-9bqp8iu7v9se';
$agent = 'Transmission/2.92';
$rep = new AgentAllowRepository();
$r = $rep->checkClient($peerId, $agent, true);
dd($r);
}

View File

@@ -4,18 +4,47 @@ namespace App\Http\Controllers;
use App\Http\Resources\AgentAllowResource;
use App\Models\AgentAllow;
use App\Repositories\AgentAllowRepository;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class AgentAllowController extends Controller
{
private $repository;
public function __construct(AgentAllowRepository $repository)
{
$this->repository = $repository;
}
private function getRules(): array
{
return [
'family' => 'required|string',
'start_name' => 'required|string',
'peer_id_pattern' => 'required|string',
'peer_id_match_num' => 'required|numeric',
'peer_id_matchtype' => ['required', Rule::in(array_keys(AgentAllow::$matchTypes))],
'peer_id_start' => 'required|string',
'agent_pattern' => 'required|string',
'agent_match_num' => 'required|numeric',
'agent_matchtype' => ['required', Rule::in(array_keys(AgentAllow::$matchTypes))],
'agent_start' => 'required|string',
'exception' => ['required', Rule::in(['yes', 'no'])],
'allowhttps' => ['required', Rule::in(['yes', 'no'])],
];
}
/**
* Display a listing of the resource.
*
* @return array
*/
public function index()
public function index(Request $request)
{
$result = AgentAllow::query()->orderBy('id', 'desc')->paginate();
$result = $this->repository->getList($request->all());
$resource = AgentAllowResource::collection($result);
return $this->success($resource);
}
@@ -28,7 +57,10 @@ class AgentAllowController extends Controller
*/
public function store(Request $request)
{
//
$request->validate($this->getRules());
$result = $this->repository->store($request->all());
$resource = new AgentAllowResource($result);
return $this->success($resource);
}
/**
@@ -53,8 +85,8 @@ class AgentAllowController extends Controller
*/
public function update(Request $request, $id)
{
$result = AgentAllow::query()->findOrFail($id);
$result->update($request->all());
$request->validate($this->getRules());
$result = $this->repository->update($request->all(), $id);
$resource = new AgentAllowResource($result);
return $this->success($resource);
}
@@ -67,8 +99,24 @@ class AgentAllowController extends Controller
*/
public function destroy($id)
{
$result = AgentAllow::query()->findOrFail($id);
$deleted = $result->delete();
return $this->success([$deleted]);
$result = $this->repository->delete($id);
return $this->success($result);
}
public function all()
{
$result = AgentAllow::query()->orderBy('id', 'desc')->get();
$resource = AgentAllowResource::collection($result);
return $this->success($resource);
}
public function check(Request $request)
{
$request->validate([
'peer_id' => 'required|string',
'agent' => 'required|string',
]);
$result = $this->repository->checkClient($request->peer_id, $request->agent, true);
return $this->success($result->toArray(), sprintf("Congratulations! the client is allowed by ID: %s", $result->id));
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\AgentDenyResource;
use App\Models\AgentDeny;
use App\Repositories\AgentDenyRepository;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class AgentDenyController extends Controller
{
private $repository;
public function __construct(AgentDenyRepository $repository)
{
$this->repository = $repository;
}
private function getRules(): array
{
return [
'family_id' => 'required|numeric',
'name' => 'required|string',
'peer_id' => 'required|string',
'agent' => 'required|string',
'comment' => 'required|string',
];
}
/**
* Display a listing of the resource.
*
* @return array
*/
public function index(Request $request)
{
$result = $this->repository->getList($request->all());
$resource = AgentDenyResource::collection($result);
return $this->success($resource);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate($this->getRules());
$result = $this->repository->store($request->all());
$resource = new AgentDenyResource($result);
return $this->success($resource);
}
/**
* Display the specified resource.
*
* @param int $id
* @return array
*/
public function show($id)
{
$result = AgentDeny::query()->findOrFail($id);
$resource = new AgentDenyResource($result);
return $this->success($resource);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function update(Request $request, $id)
{
$request->validate($this->getRules());
$result = $this->repository->update($request->all(), $id);
$resource = new AgentDenyResource($result);
return $this->success($resource);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return array
*/
public function destroy($id)
{
$result = $this->repository->delete($id);
return $this->success($result);
}
}

View File

@@ -20,7 +20,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AgentDenyResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'family_id' => $this->family_id,
'agent' => $this->agent,
'peer_id' => $this->peer_id,
'comment' => $this->comment,
'name' => $this->name,
'family' => new AgentAllowResource($this->whenLoaded('family'))
];
}
}

View File

@@ -6,11 +6,24 @@ class AgentAllow extends NexusModel
{
protected $table = 'agent_allowed_family';
public $timestamps = true;
protected $fillable = [
'family', 'start_name', 'exception', 'allowhttps', 'comment',
'peer_id_pattern', 'peer_id_match_num', 'peer_id_matchtype', 'peer_id_start',
'agent_pattern', 'agent_match_num', 'agent_matchtype', 'agent_start',
];
const MATCH_TYPE_DEC = 'dec';
const MATCH_TYPE_HEX = 'hex';
public static $matchTypes = [
self::MATCH_TYPE_DEC => 'dec',
self::MATCH_TYPE_HEX => 'hex',
];
public function denies(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(AgentDeny::class, 'family_id');
}
}

17
app/Models/AgentDeny.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
namespace App\Models;
class AgentDeny extends NexusModel
{
protected $table = 'agent_allowed_exception';
protected $fillable = [
'family_id', 'name', 'peer_id', 'agent', 'comment'
];
public function family(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(AgentAllow::class, 'family_id');
}
}

View File

@@ -0,0 +1,243 @@
<?php
namespace App\Repositories;
use App\Exceptions\NexusException;
use App\Models\AgentAllow;
use App\Models\AgentDeny;
class AgentAllowRepository extends BaseRepository
{
public function getList(array $params)
{
$query = AgentAllow::query();
if (!empty($params['family'])) {
$query->where('family', 'like', "%{$params['family']}%");
}
list($sortField, $sortType) = $this->getSortFieldAndType($params);
$query->orderBy($sortField, $sortType);
return $query->paginate();
}
public function store(array $params)
{
$this->getPatternMatches($params['peer_id_pattern'], $params['peer_id_start'], $params['peer_id_match_num']);
$this->getPatternMatches($params['agent_pattern'], $params['agent_start'], $params['agent_match_num']);
$model = AgentAllow::query()->create($params);
return $model;
}
public function update(array $params, $id)
{
$this->getPatternMatches($params['peer_id_pattern'], $params['peer_id_start'], $params['peer_id_match_num']);
$this->getPatternMatches($params['agent_pattern'], $params['agent_start'], $params['agent_match_num']);
$model = AgentAllow::query()->findOrFail($id);
$model->update($params);
return $model;
}
public function getDetail($id)
{
$model = AgentAllow::query()->findOrFail($id);
return $model;
}
public function delete($id)
{
$model = AgentAllow::query()->findOrFail($id);
$model->denies()->delete();
$result = $model->delete();
return $result;
}
public function getPatternMatches($pattern, $start, $matchNum)
{
if (!preg_match($pattern, $start, $matches)) {
throw new NexusException(sprintf('pattern: %s can not match start: %s', $pattern, $start));
}
$matchCount = count($matches) - 1;
if ($matchNum > $matchCount) {
throw new NexusException("pattern: $pattern match start: $start got matches count: $matchCount, but require $matchNum.");
}
return array_slice($matches, 1, $matchNum);
}
public function checkClient($peerId, $agent, $debug = false)
{
//check from high version to low version, if high version allow, stop!
$allows = AgentAllow::query()
->orderBy('peer_id_start', 'desc')
->orderBy('agent_start', 'desc')
->get();
$agentAllowPassed = null;
$versionTooLowStr = '';
foreach ($allows as $agentAllow) {
$agentAllowId = $agentAllow->id;
$isPeerIdAllowed = $isAgentAllowed = $isPeerIdTooLow = $isAgentTooLow = false;
//check peer_id
if ($agentAllow->peer_id_pattern == '') {
$isPeerIdAllowed = true;
} else {
$pattern = $agentAllow->peer_id_pattern;
$start = $agentAllow->peer_id_start;
$matchType = $agentAllow->peer_id_matchtype;
$matchNum = $agentAllow->peer_id_match_num;
try {
$peerIdResult = $this->isAllowed($pattern, $start, $matchNum, $matchType, $peerId, $debug);
if ($debug) {
do_log(
"agentAllowId: $agentAllowId, peerIdResult: $peerIdResult, with parameters: "
. nexus_json_encode(compact('pattern', 'start', 'matchNum', 'matchType', 'peerId'))
);
}
} catch (\Exception $exception) {
do_log("agent allow: {$agentAllow->id} check peer_id error: " . $exception->getMessage(), 'error');
throw new NexusException("regular expression err for peer_id: " . $start . ", please ask sysop to fix this");
}
if ($peerIdResult == 1) {
$isPeerIdAllowed = true;
}
if ($peerIdResult == 2) {
$isPeerIdTooLow = true;
}
}
//check agent
if ($agentAllow->agent_pattern == '') {
$isAgentAllowed = true;
} else {
$pattern = $agentAllow->agent_pattern;
$start = $agentAllow->agent_start;
$matchType = $agentAllow->agent_matchtype;
$matchNum = $agentAllow->agent_match_num;
try {
$agentResult = $this->isAllowed($pattern, $start, $matchNum, $matchType, $agent, $debug);
if ($debug) {
do_log(
"agentAllowId: $agentAllowId, agentResult: $agentResult, with parameters: "
. nexus_json_encode(compact('pattern', 'start', 'matchNum', 'matchType', 'agent'))
);
}
} catch (\Exception $exception) {
do_log("agent allow: {$agentAllow->id} check agent error: " . $exception->getMessage(), 'error');
throw new NexusException("regular expression err for agent: " . $start . ", please ask sysop to fix this");
}
if ($agentResult == 1) {
$isAgentAllowed = true;
}
if ($agentResult == 2) {
$isAgentTooLow = true;
}
}
//both OK, passed, client is allowed
if ($isPeerIdAllowed && $isAgentAllowed) {
$agentAllowPassed = $agentAllow;
break;
}
if ($isPeerIdTooLow && $isAgentTooLow) {
$versionTooLowStr = "Your " . $agentAllow->family . " 's version is too low, please update it after " . $agentAllow->start_name;
}
}
if ($versionTooLowStr) {
throw new NexusException($versionTooLowStr);
}
if (!$agentAllowPassed) {
throw new NexusException("Banned Client, Please goto " . getSchemeAndHttpHost() . "/faq.php#id29 for a list of acceptable clients");
}
if ($debug) {
do_log("agentAllowPassed: " . $agentAllowPassed->toJson());
}
// check if exclude
if ($agentAllowPassed->exception == 'yes') {
$agentDeny = $this->checkIsDenied($peerId, $agent, $agentAllowPassed->id);
if ($agentDeny) {
if ($debug) {
do_log("agentDeny: " . $agentDeny->toJson());
}
throw new NexusException(sprintf(
"[%s-%s]Client: %s is banned due to: %s",
$agentAllowPassed->id, $agentDeny->id, $agentDeny->name, $agentDeny->comment
));
}
}
if (isHttps() && $agentAllowPassed->allowhttps != 'yes') {
throw new NexusException(sprintf(
"[%s]This client does not support https well, Please goto %s/faq.php#id29 for a list of proper clients",
$agentAllowPassed->id, getSchemeAndHttpHost()
));
}
return $agentAllowPassed;
}
private function checkIsDenied($peerId, $agent, $familyId)
{
$agentDenies = AgentDeny::query()->where('family_id', $familyId)->get();
foreach ($agentDenies as $agentDeny) {
if ($agentDeny->agent == $agent && preg_match("/^" . $agentDeny->peer_id . "/", $peerId)) {
return $agentDeny;
}
}
}
/**
* check peer_id or agent is allowed
*
* 0: not allowed
* 1: allowed
* 2: version too low
*
* @param $pattern
* @param $start
* @param $matchNum
* @param $matchType
* @param $value
* @param bool $debug
* @return int
* @throws NexusException
*/
private function isAllowed($pattern, $start, $matchNum, $matchType, $value, $debug = false): int
{
$matchBench = $this->getPatternMatches($pattern, $start, $matchNum);
if ($debug) {
do_log("matchBench: " . nexus_json_encode($matchBench));
}
if (!preg_match($pattern, $value, $matchTarget)) {
return 0;
}
if ($matchNum <= 0) {
return 1;
}
$matchTarget = array_slice($matchTarget, 1);
if ($debug) {
do_log("matchTarget: " . nexus_json_encode($matchTarget));
}
for ($i = 0; $i < $matchNum; $i++) {
if (!isset($matchBench[$i]) || !isset($matchTarget[$i])) {
break;
}
if ($matchType == 'dec') {
$matchBench[$i] = intval($matchBench[$i]);
$matchTarget[$i] = intval($matchTarget[$i]);
} elseif ($matchType == 'hex') {
$matchBench[$i] = hexdec($matchBench[$i]);
$matchTarget[$i] = hexdec($matchTarget[$i]);
} else {
throw new NexusException(sprintf("Invalid match type: %s", $matchType));
}
if ($matchTarget[$i] > $matchBench[$i]) {
return 1;
} elseif ($matchTarget[$i] < $matchBench[$i]) {
return 2;
}
}
return 0;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Repositories;
use App\Models\AgentDeny;
class AgentDenyRepository extends BaseRepository
{
public function getList(array $params)
{
$query = AgentDeny::query()->with(['family']);
if (!empty($params['family_id'])) {
$query->where('family_id', $params['family_id']);
}
$query->orderBy('family_id', 'desc');
return $query->paginate();
}
public function store(array $params)
{
$model = AgentDeny::query()->create($params);
return $model;
}
public function update(array $params, $id)
{
$model = AgentDeny::query()->findOrFail($id);
$model->update($params);
return $model;
}
public function getDetail($id)
{
$model = AgentDeny::query()->findOrFail($id);
return $model;
}
public function delete($id)
{
$model = AgentDeny::query()->findOrFail($id);
$result = $model->delete();
return $result;
}
}

View File

@@ -16,6 +16,15 @@ class UserRepository extends BaseRepository
public function getList(array $params)
{
$query = User::query();
if (!empty($params['id'])) {
$query->where('id', $params['id']);
}
if (!empty($params['username'])) {
$query->where('username', 'like',"%{$params['username']}%");
}
if (!empty($params['email'])) {
$query->where('email', 'like',"%{$params['email']}%");
}
list($sortField, $sortType) = $this->getSortFieldAndType($params);
$query->orderBy($sortField, $sortType);
return $query->paginate();

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIdToAgentAllowedExceptionTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('agent_allowed_exception', function (Blueprint $table) {
$table->increments('id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('agent_allowed_exception', function (Blueprint $table) {
$table->dropColumn('id');
});
}
}

View File

@@ -511,6 +511,7 @@ function api(...$args)
'msg' => (string)$msg,
'data' => $data,
'time' => (float)number_format(microtime(true) - $start, 3),
'request_id' => REQUEST_ID,
];
}

View File

@@ -36,6 +36,9 @@ Route::group(['middleware' => ['auth:sanctum', 'locale']], function () {
Route::group(['middleware' => ['admin']], function () {
Route::resource('agent-allows', \App\Http\Controllers\AgentAllowController::class);
Route::get('all-agent-allows', [\App\Http\Controllers\AgentAllowController::class, 'all']);
Route::post('agent-check', [\App\Http\Controllers\AgentAllowController::class, 'check']);
Route::resource('agent-denies', \App\Http\Controllers\AgentDenyController::class);
Route::resource('users', \App\Http\Controllers\UserController::class);
Route::get('user-base', [\App\Http\Controllers\UserController::class, 'base']);