support add user

This commit is contained in:
jb-alvarado 2023-09-26 09:29:46 +02:00
parent 2ca3aa73b2
commit 70e87dce04
10 changed files with 753 additions and 589 deletions

View File

@ -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>

View File

@ -78,7 +78,7 @@ const router = useRouter()
function logout() {
authStore.removeToken()
authStore.updateIsLogin(false)
authStore.isLogin = false
router.push({ path: '/' })
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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
}

View File

@ -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
}
},
},

View File

@ -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
},
},
})

View File

@ -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 = []
})
},

View File

@ -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