support add user
This commit is contained in:
parent
2ca3aa73b2
commit
70e87dce04
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div class="container">
|
||||
<h2 class="pb-4 pt-3">Channel Configuration</h2>
|
||||
<div style="width: 100%; height: 43px">
|
||||
<div class="w-100" style="height: 43px">
|
||||
<div class="float-end">
|
||||
<button class="btn btn-primary" @click="addChannel()">Add new Channel</button>
|
||||
</div>
|
||||
|
@ -78,7 +78,7 @@ const router = useRouter()
|
||||
|
||||
function logout() {
|
||||
authStore.removeToken()
|
||||
authStore.updateIsLogin(false)
|
||||
authStore.isLogin = false
|
||||
router.push({ path: '/' })
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,19 @@
|
||||
<div>
|
||||
<div class="container">
|
||||
<h2 class="pb-4 pt-3">User Configuration</h2>
|
||||
<div class="w-100" style="height: 43px">
|
||||
<div class="float-end">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
title="Add new User"
|
||||
data-tooltip="tooltip"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#userModal"
|
||||
>
|
||||
Add User
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form v-if="configStore.configUser" @submit.prevent="onSubmitUser">
|
||||
<div class="mb-3 row">
|
||||
<label for="userName" class="col-sm-2 col-form-label">Username</label>
|
||||
@ -18,7 +31,7 @@
|
||||
<div class="mb-3 row">
|
||||
<label for="userMail" class="col-sm-2 col-form-label">mail</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="userMail" v-model="configStore.configUser.mail" />
|
||||
<input type="email" class="form-control" id="userMail" v-model="configStore.configUser.mail" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
@ -40,6 +53,63 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="userModal" ref="userModal" class="modal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="userModalLabel">Add User</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
|
||||
</div>
|
||||
<form @reset="clearUser" @submit.prevent="addUser">
|
||||
<div class="modal-body">
|
||||
<label for="name-input" class="form-label">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="name-input"
|
||||
aria-describedby="Username"
|
||||
v-model.number="user.username"
|
||||
required
|
||||
/>
|
||||
<label for="mail-input" class="form-label mt-2">Mail</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="mail-input"
|
||||
aria-describedby="Mail Address"
|
||||
v-model.number="user.mail"
|
||||
required
|
||||
/>
|
||||
<label for="pass-input" class="form-label mt-2">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="pass-input"
|
||||
aria-describedby="Password"
|
||||
v-model.number="user.password"
|
||||
required
|
||||
/>
|
||||
<label for="confirm-input" class="form-label mt-2">Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="confirm-input"
|
||||
aria-describedby="Confirm Password"
|
||||
v-model.number="user.confirm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-primary" data-bs-dismiss="modal" aria-label="Cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">Ok</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -52,9 +122,59 @@ const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const indexStore = useIndex()
|
||||
|
||||
const { $bootstrap } = useNuxtApp()
|
||||
|
||||
const userModal = ref()
|
||||
const newPass = ref('')
|
||||
const confirmPass = ref('')
|
||||
|
||||
const user = ref({
|
||||
username: '',
|
||||
mail: '',
|
||||
password: '',
|
||||
confirm: '',
|
||||
role_id: 1,
|
||||
})
|
||||
|
||||
async function clearUser() {
|
||||
user.value.username = ''
|
||||
user.value.mail = ''
|
||||
user.value.password = ''
|
||||
user.value.confirm = ''
|
||||
user.value.role_id = 1
|
||||
}
|
||||
|
||||
async function addUser() {
|
||||
if (user.value.password === user.value.confirm) {
|
||||
// @ts-ignore
|
||||
const modal = $bootstrap.Modal.getOrCreateInstance(userModal.value)
|
||||
modal.hide()
|
||||
|
||||
authStore.inspectToken()
|
||||
const update = await configStore.addNewUser(user.value)
|
||||
|
||||
if (update.status === 200) {
|
||||
indexStore.alertVariant = 'alert-success'
|
||||
indexStore.alertMsg = 'Add user success!'
|
||||
} else {
|
||||
indexStore.alertVariant = 'alert-danger'
|
||||
indexStore.alertMsg = 'Add user failed!'
|
||||
}
|
||||
|
||||
clearUser()
|
||||
|
||||
} else {
|
||||
indexStore.alertVariant = 'alert-danger'
|
||||
indexStore.alertMsg = 'Password mismatch!'
|
||||
}
|
||||
|
||||
indexStore.showAlert = true
|
||||
|
||||
setTimeout(() => {
|
||||
indexStore.showAlert = false
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
async function onSubmitUser() {
|
||||
if (newPass && newPass.value === confirmPass.value) {
|
||||
configStore.configUser.password = newPass.value
|
||||
|
1101
package-lock.json
generated
1101
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ffplayout-frontend",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"description": "Web GUI for ffplayout",
|
||||
"author": "Jonathan Baecker",
|
||||
"private": true,
|
||||
@ -17,10 +17,10 @@
|
||||
"@pinia/nuxt": "^0.4.11",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"bootstrap": "^5.3.1",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.1",
|
||||
"cookie-universal-nuxt": "^2.2.2",
|
||||
"dayjs": "^1.11.9",
|
||||
"dayjs": "^1.11.10",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mpegts.js": "^1.7.3",
|
||||
@ -33,17 +33,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/eslint-config": "^12.0.0",
|
||||
"@types/bootstrap": "^5.2.6",
|
||||
"@types/lodash": "^4.14.198",
|
||||
"@types/splitpanes": "^2.2.1",
|
||||
"@types/bootstrap": "^5.2.7",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"@types/splitpanes": "^2.2.2",
|
||||
"@types/video.js": "^7.3.52",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-plugin-nuxt": "^4.0.0",
|
||||
"fibers": "^5.0.3",
|
||||
"nuxt": "3.7.1",
|
||||
"postcss": "^8.4.29",
|
||||
"nuxt": "3.7.4",
|
||||
"postcss": "^8.4.30",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"sass": "^1.66.1",
|
||||
"sass": "^1.68.0",
|
||||
"sass-loader": "^13.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ async function login() {
|
||||
async function logout() {
|
||||
try {
|
||||
authStore.removeToken()
|
||||
authStore.updateIsLogin(false)
|
||||
authStore.isLogin = false
|
||||
} catch (e) {
|
||||
formError.value = e as string
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import jwtDecode, { JwtPayload } from 'jwt-decode'
|
||||
|
||||
interface JwtPayloadExt extends JwtPayload {
|
||||
role: string
|
||||
}
|
||||
|
||||
export const useAuth = defineStore('auth', {
|
||||
state: () => ({
|
||||
isLogin: false,
|
||||
jwtToken: '',
|
||||
authHeader: {},
|
||||
role: '',
|
||||
}),
|
||||
|
||||
getters: {},
|
||||
@ -22,10 +27,6 @@ export const useAuth = defineStore('auth', {
|
||||
this.authHeader = { Authorization: `Bearer ${token}` }
|
||||
},
|
||||
|
||||
updateIsLogin(bool: boolean) {
|
||||
this.isLogin = bool
|
||||
},
|
||||
|
||||
removeToken() {
|
||||
const cookie = useCookie('token')
|
||||
cookie.value = null
|
||||
@ -52,7 +53,9 @@ export const useAuth = defineStore('auth', {
|
||||
.then((response) => response.json())
|
||||
.then((response) => {
|
||||
this.updateToken(response.user.token)
|
||||
this.updateIsLogin(true)
|
||||
const decodedToken = jwtDecode<JwtPayloadExt>(response.user.token)
|
||||
this.isLogin = true
|
||||
this.role = decodedToken.role
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.status) {
|
||||
@ -72,18 +75,19 @@ export const useAuth = defineStore('auth', {
|
||||
|
||||
if (token) {
|
||||
this.updateToken(token)
|
||||
const decodedToken = jwtDecode<JwtPayload>(token)
|
||||
const decodedToken = jwtDecode<JwtPayloadExt>(token)
|
||||
const timestamp = Date.now() / 1000
|
||||
const expireToken = decodedToken.exp
|
||||
this.role = decodedToken.role
|
||||
|
||||
if (expireToken && this.jwtToken && expireToken - timestamp > 15) {
|
||||
this.updateIsLogin(true)
|
||||
this.isLogin = true
|
||||
} else {
|
||||
// Prompt user to re login.
|
||||
this.updateIsLogin(false)
|
||||
this.isLogin = false
|
||||
}
|
||||
} else {
|
||||
this.updateIsLogin(false)
|
||||
this.isLogin = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -6,26 +6,11 @@ const { timeToSeconds } = stringFormatter()
|
||||
import { useAuth } from '~/stores/auth'
|
||||
import { useIndex } from '~/stores/index'
|
||||
|
||||
interface GuiConfig {
|
||||
id: number
|
||||
config_path: string
|
||||
extra_extensions: string | string[]
|
||||
name: string
|
||||
preview_url: string
|
||||
service: string
|
||||
uts_offset?: number
|
||||
}
|
||||
|
||||
interface User {
|
||||
username: string
|
||||
mail: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
export const useConfig = defineStore('config', {
|
||||
state: () => ({
|
||||
configID: 0,
|
||||
configCount: 0,
|
||||
contentType: { 'content-type': 'application/json;charset=UTF-8' },
|
||||
configGui: [] as GuiConfig[],
|
||||
configGuiRaw: [] as GuiConfig[],
|
||||
startInSec: 0,
|
||||
@ -101,19 +86,18 @@ export const useConfig = defineStore('config', {
|
||||
async setGuiConfig(obj: GuiConfig): Promise<any> {
|
||||
const authStore = useAuth()
|
||||
const stringObj = _.cloneDeep(obj)
|
||||
const contentType = { 'content-type': 'application/json;charset=UTF-8' }
|
||||
let response
|
||||
|
||||
if (this.configGuiRaw.some((e) => e.id === stringObj.id)) {
|
||||
response = await fetch(`/api/channel/${obj.id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
headers: { ...this.contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify(stringObj),
|
||||
})
|
||||
} else {
|
||||
response = await fetch('/api/channel/', {
|
||||
method: 'POST',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
headers: { ...this.contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify(stringObj),
|
||||
})
|
||||
|
||||
@ -173,7 +157,6 @@ export const useConfig = defineStore('config', {
|
||||
async setPlayoutConfig(obj: any) {
|
||||
const authStore = useAuth()
|
||||
const channel = this.configGui[this.configID].id
|
||||
const contentType = { 'content-type': 'application/json;charset=UTF-8' }
|
||||
|
||||
this.startInSec = timeToSeconds(obj.playlist.day_start)
|
||||
this.playlistLength = timeToSeconds(obj.playlist.length)
|
||||
@ -184,7 +167,7 @@ export const useConfig = defineStore('config', {
|
||||
|
||||
const update = await fetch(`/api/playout/config/${channel}`, {
|
||||
method: 'PUT',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
headers: { ...this.contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify(obj),
|
||||
})
|
||||
|
||||
@ -207,15 +190,27 @@ export const useConfig = defineStore('config', {
|
||||
|
||||
async setUserConfig(obj: any) {
|
||||
const authStore = useAuth()
|
||||
const contentType = { 'content-type': 'application/json;charset=UTF-8' }
|
||||
|
||||
const update = await fetch(`/api/user/${obj.id}`, {
|
||||
method: 'PUT',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
headers: { ...this.contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify(obj),
|
||||
})
|
||||
|
||||
return update
|
||||
},
|
||||
|
||||
async addNewUser(user: User) {
|
||||
const authStore = useAuth()
|
||||
delete user.confirm
|
||||
|
||||
const update = await fetch('/api/user/', {
|
||||
method: 'Post',
|
||||
headers: { ...this.contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify(user),
|
||||
})
|
||||
|
||||
return update
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -30,10 +30,6 @@ export const usePlaylist = defineStore('playlist', {
|
||||
|
||||
getters: {},
|
||||
actions: {
|
||||
updatePlaylist(list: any) {
|
||||
this.playlist = list
|
||||
},
|
||||
|
||||
async getPlaylist(date: string) {
|
||||
const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
@ -52,13 +48,11 @@ export const usePlaylist = defineStore('playlist', {
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.program) {
|
||||
this.updatePlaylist(
|
||||
processPlaylist(configStore.startInSec, configStore.playlistLength, data.program, false)
|
||||
)
|
||||
this.playlist = processPlaylist(configStore.startInSec, configStore.playlistLength, data.program, false)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.updatePlaylist([])
|
||||
this.playlist = []
|
||||
})
|
||||
},
|
||||
|
||||
|
18
types/index.ts → types/index.d.ts
vendored
18
types/index.ts → types/index.d.ts
vendored
@ -1,6 +1,24 @@
|
||||
export { }
|
||||
|
||||
declare global {
|
||||
interface GuiConfig {
|
||||
id: number
|
||||
config_path: string
|
||||
extra_extensions: string | string[]
|
||||
name: string
|
||||
preview_url: string
|
||||
service: string
|
||||
uts_offset?: number
|
||||
}
|
||||
|
||||
interface User {
|
||||
username: String
|
||||
mail: String
|
||||
password?: String
|
||||
confirm?: String
|
||||
role_id?: Number
|
||||
}
|
||||
|
||||
interface Crumb {
|
||||
text: string
|
||||
path: string
|
Loading…
x
Reference in New Issue
Block a user