This commit is contained in:
Karolis2011
2021-12-23 06:42:40 +02:00
parent cad4268b79
commit b367854887
23 changed files with 4103 additions and 2049 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -8,27 +8,29 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@azure/msal-browser": "^2.18.0", "@azure/msal-browser": "^2.20.0",
"@popperjs/core": "^2.10.1", "@popperjs/core": "^2.11.0",
"axios": "^0.20.0-0", "axios": "^0.20.0-0",
"bootstrap": "^5.1.1", "bootstrap": "^5.1.3",
"bootstrap-icons": "^1.7.2",
"cookies-js": "^1.2.3", "cookies-js": "^1.2.3",
"core-js": "^3.7.0", "core-js": "^3.20.0",
"joi": "^17.5.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"vue": "^3.0.2", "vue": "^3.2.26",
"vue-loader-v16": "npm:vue-loader@^16.0.0-alpha.3", "vue-loader-v16": "npm:vue-loader@^16.8.3",
"vue-router": "^4.0.0-rc.5", "vue-router": "^4.0.12",
"vuex": "^4.0.2" "vuex": "^4.0.2"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^4.5.9", "@vue/cli-plugin-babel": "^4.5.15",
"@vue/cli-plugin-eslint": "^4.5.9", "@vue/cli-plugin-eslint": "^4.5.15",
"@vue/cli-service": "^4.5.9", "@vue/cli-service": "^4.5.15",
"@vue/compiler-sfc": "^3.0.2", "@vue/compiler-sfc": "^3.2.26",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-plugin-vue": "^7.1.0", "eslint-plugin-vue": "^7.20.0",
"sass": "^1.33.0", "sass": "^1.45.1",
"sass-loader": "^10.2.0" "sass-loader": "^10.2.0"
}, },
"eslintConfig": { "eslintConfig": {

View File

@@ -1,5 +1,6 @@
<template> <template>
<nav-menu></nav-menu> <nav-menu />
<data-alert />
<div v-if="isLocal" class="container"> <div v-if="isLocal" class="container">
<div class="alert alert-danger"> <div class="alert alert-danger">
<h4 class="alert-heading">Lokali sistemą aptikta</h4> <h4 class="alert-heading">Lokali sistemą aptikta</h4>
@@ -19,16 +20,20 @@
</span> </span>
</div> </div>
</div> </div>
<transition name="slide-fade">
<router-view /> <router-view />
</transition>
</template> </template>
<script> <script>
import NavMenu from './components/NavMenu.vue' import NavMenu from './components/NavMenu.vue'
import DataAlert from './components/DataAlert.vue'
export default { export default {
name: 'App', name: 'App',
components: { components: {
NavMenu, NavMenu,
DataAlert,
}, },
data() { data() {
return { return {

View File

@@ -1 +1,5 @@
 $font-family-base: 'Open Sans', sans-serif;
$primary: #1862a1;
$danger: #a1181d;
$warning: #eb7d1d;

View File

@@ -1,2 +1,33 @@
@import "custom"; @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap');
@import "~bootstrap/scss/bootstrap";
@import 'custom';
@import '~bootstrap/scss/bootstrap';
@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css');
/* Enter and leave animations can use different */
/* durations and timing functions. */
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-leave-active,
.slide-fade-enter-active {
position: relative;
top: 0;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
.slide-fade-enter-to,
.slide-fade-leave-from {
transform: translateX(0px);
opacity: 1;
}

View File

@@ -0,0 +1,16 @@
import Axios from 'axios'
import store from './store'
export const authAxios = Axios.create({})
export const axios = Axios
authAxios.interceptors.request.use(
async (config) => {
await store.dispatch('msalAuth/waitTillReady') // Delay requesrt till we ready
config.headers['Authorization'] = `Bearer ${store.state.msalAuth.idToken}`
return config
},
() => {}
)
export default Axios

View File

@@ -0,0 +1,58 @@
<template>
<div>
<div ref="modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Duomenų naudojimo pranešimas</h5>
</div>
<div class="modal-body">
<p>
Norime jus informuoti, kad sutikdami su šituo duomenų sutikimo jus
KTU studentų atstovybei sutinkate perduoti savo duomenis, tokius
kaip el. pašto adresas, indentifikacijos kodas, jūsų vardas ir
pavardė, ir kitus svarbūs identifikavimo duomenis apie jus.
</p>
<p>
Norime informuoti, kad ši programa naudoja sausainiukus ir vietinę
saugyklą, kad užtikrintų sklandų veikimą.
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" @click="confirm">
Sutinku
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { Modal } from 'bootstrap'
export default {
data() {
return {
modal: null,
}
},
methods: {
confirm() {
this.modal.hide()
window.localStorage.setItem('conf', 'true')
},
},
mounted() {
var el = this.$refs.modal
this.modal = new Modal(el, {
keyboard: false,
backdrop: 'static',
})
if (!window.localStorage.getItem('conf')) {
this.modal.show()
}
},
}
</script>

View File

@@ -32,6 +32,21 @@
>Pateikti problemą</router-link >Pateikti problemą</router-link
> >
</li> </li>
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
<router-link :to="{ name: 'Problems' }" class="nav-link"
>Problemos</router-link
>
</li>
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
<router-link :to="{ name: 'Feedbacks' }" class="nav-link"
>Atsiliepimai</router-link
>
</li>
<li class="nav-item" v-if="$store.state.msalAuth.isAdmin">
<router-link :to="{ name: 'Issues' }" class="nav-link"
>Pateiktos problemos</router-link
>
</li>
<!-- <li class="nav-item"> <!-- <li class="nav-item">
<a href="/swagger" class="nav-link">Swagger UI</a> <a href="/swagger" class="nav-link">Swagger UI</a>
</li> --> </li> -->

View File

@@ -0,0 +1,34 @@
<template>
<div class="container">
<div class="card my-4" v-for="f in feedbacks" :key="f.id">
<div class="row g-0">
<div class="card-body">
<h6 class="card-text text-muted">Paskelbta: {{ f.created }}</h6>
<h5 class="card-title">Atsiliepimas:</h5>
<p class="card-text">{{ f.feedbackLt }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
import { axios } from '@/axios'
export default {
data() {
return {
feedbacks: [],
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
const response = await axios.get('/api/PublishedFeedbacks')
this.feedbacks = response.data
},
},
}
</script>

View File

@@ -12,9 +12,7 @@
</div> </div>
<template v-if="$store.state.msalAuth.isAdmin"> <template v-if="$store.state.msalAuth.isAdmin">
<div class="alert alert-warning"> <div class="alert alert-warning">
<span> <span> Tu esi administratorius. </span>
Tu esi administratorius.
</span>
</div> </div>
<h3>Prieigos raktas (Access token)</h3> <h3>Prieigos raktas (Access token)</h3>
<a <a
@@ -59,7 +57,7 @@
</template> </template>
<script> <script>
import axios from 'axios' import { authAxios } from '@/axios'
export default { export default {
data() { data() {
@@ -70,13 +68,9 @@ export default {
methods: { methods: {
serverVerify() { serverVerify() {
this.verificationResult = null this.verificationResult = null
axios authAxios
.get('/api/AuthMetadata/authed', { .get('/api/AuthMetadata/authed')
headers: { .then((response) => {
Authorization: `Bearer ${this.$store.state.msalAuth.idToken}`,
},
})
.then(response => {
this.verificationResult = response.data this.verificationResult = response.data
}) })
.catch(function (error) { .catch(function (error) {
@@ -85,13 +79,9 @@ export default {
}, },
serverAdminVerify() { serverAdminVerify() {
this.verificationResult = null this.verificationResult = null
axios authAxios
.get('/api/AuthMetadata/Admin', { .get('/api/AuthMetadata/Admin')
headers: { .then((response) => {
Authorization: `Bearer ${this.$store.state.msalAuth.idToken}`,
},
})
.then(response => {
this.verificationResult = response.data this.verificationResult = response.data
}) })
.catch(function (error) { .catch(function (error) {

View File

@@ -0,0 +1,78 @@
<template>
<div class="container">
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
<div class="alert alert-danger" v-if="error">{{ error }}</div>
<div class="card my-4" v-for="i in issues" :key="i.id">
<div class="row g-0">
<div class="card-body">
<h6 class="card-text text-muted">Pateikta: {{ i.created }}</h6>
<h6 class="card-text text-muted">El paštas: {{ i.email }}</h6>
<h6 class="card-text text-muted">
Skelbtinas: {{ i.publishable ? '✔' : '❌' }}
</h6>
<h6 class="card-text text-muted">
Išsprestą: {{ i.solved ? '✔' : '❌' }}
</h6>
<h6 class="card-text text-muted">
Tipas: {{ issueTypes[i.issueTypeId]?.name }}
</h6>
<h5 class="card-title">Aprašymas:</h5>
<p class="card-text">{{ i.description }}</p>
<button class="btn btn-danger mx-1" @click="deleteIssue(i.id)">
<i class="bi bi-trash-fill"></i>
Ištrinti
</button>
<router-link
:to="{ name: 'IssueNewProblem', params: { id: i.id } }"
class="btn btn-primary mx-1"
>Paskelbti problemą</router-link
>
<router-link
:to="{ name: 'IssueNewFeedback', params: { id: i.id } }"
class="btn btn-primary mx-1"
>Paskelbti atsiliepimą</router-link
>
</div>
</div>
</div>
</div>
</template>
<script>
import { axios, authAxios } from '@/axios'
export default {
data() {
return {
issues: [],
issueTypes: {},
ok: null,
error: null,
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
const response = await authAxios.get('/api/Issues')
this.issues = response.data
const response2 = await axios.get('/api/IssueTypes')
response2.data.forEach((it) => {
this.issueTypes[it.id] = it
})
},
async deleteIssue(id) {
this.error = null
this.ok = null
try {
await authAxios.delete(`/api/Issues/${id}`)
this.ok = 'Sėkmingai ištrintą'
await this.fetchData()
} catch (error) {
this.error = error
}
},
},
}
</script>

View File

@@ -0,0 +1,102 @@
<template>
<div class="container">
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
<div class="alert alert-danger" v-if="error">{{ error }}</div>
<h1>Naujas atsiliepimas</h1>
<div class="card my-4">
<div class="row g-0">
<div class="card-body">
<h6 class="card-text text-muted">Pateikta: {{ issue.created }}</h6>
<h6 class="card-text text-muted">El paštas: {{ issue.email }}</h6>
<h6 class="card-text text-muted">
Skelbtinas: {{ issue.publishable ? '✔' : '❌' }}
</h6>
<h6 class="card-text text-muted">
Išsprestą: {{ issue.solved ? '✔' : '❌' }}
</h6>
<h6 class="card-text text-muted">
Tipas: {{ issueTypes[issue.issueTypeId]?.name }}
</h6>
<h5 class="card-title">Aprašymas:</h5>
<p class="card-text">{{ issue.description }}</p>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="card col-lg-6 p-5">
<div class="mb-3">
<label for="problemLtTextArea" class="form-label"
>Lietuviškas Aprašymas</label
>
<textarea
v-model="feedback.feedbackLt"
class="form-control"
id="problemLtTextArea"
rows="4"
></textarea>
</div>
<div class="mb-3">
<label for="problemEnTextArea" class="form-label"
>Angliškas Aprašymas</label
>
<textarea
v-model="feedback.feedbackEn"
class="form-control"
id="problemEnTextArea"
rows="4"
></textarea>
</div>
<button @click="create" class="btn btn-primary btn-lg">
Sukurti naują atsiliepimą
</button>
</div>
</div>
</div>
</template>
<script>
import { axios, authAxios } from '@/axios'
export default {
data() {
return {
issue: null,
issueTypes: {},
feedback: {
feedbackEn: '',
feedbackLt: '',
issueId: null,
},
ok: null,
error: null,
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
const response = await authAxios.get(
`/api/Issues/${this.$route.params.id}`
)
this.issue = response.data
const response2 = await axios.get('/api/IssueTypes')
response2.data.forEach((it) => {
this.issueTypes[it.id] = it
})
},
async create() {
try {
this.feedback.issueId = this.issue.id
await authAxios.post('/api/PublishedFeedbacks', this.feedback)
this.ok = 'Sukurtą.'
} catch (error) {
this.error = error
}
},
},
watch: {
$route: 'fetchData',
},
}
</script>

View File

@@ -0,0 +1,102 @@
<template>
<div class="container">
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
<div class="alert alert-danger" v-if="error">{{ error }}</div>
<h1>Nauja problema</h1>
<div class="card my-4">
<div class="row g-0">
<div class="card-body">
<h6 class="card-text text-muted">Pateikta: {{ issue.created }}</h6>
<h6 class="card-text text-muted">El paštas: {{ issue.email }}</h6>
<h6 class="card-text text-muted">
Skelbtinas: {{ issue.publishable ? '✔' : '❌' }}
</h6>
<h6 class="card-text text-muted">
Išsprestą: {{ issue.solved ? '✔' : '❌' }}
</h6>
<h6 class="card-text text-muted">
Tipas: {{ issueTypes[issue.issueTypeId]?.name }}
</h6>
<h5 class="card-title">Aprašymas:</h5>
<p class="card-text">{{ issue.description }}</p>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="card col-lg-6 p-5">
<div class="mb-3">
<label for="problemLtTextArea" class="form-label"
>Lietuviškas Aprašymas</label
>
<textarea
v-model="problem.problemLt"
class="form-control"
id="problemLtTextArea"
rows="4"
></textarea>
</div>
<div class="mb-3">
<label for="problemEnTextArea" class="form-label"
>Angliškas Aprašymas</label
>
<textarea
v-model="problem.problemEn"
class="form-control"
id="problemEnTextArea"
rows="4"
></textarea>
</div>
<button @click="create" class="btn btn-primary btn-lg">
Sukurti naują problemą
</button>
</div>
</div>
</div>
</template>
<script>
import { axios, authAxios } from '@/axios'
export default {
data() {
return {
issue: null,
issueTypes: {},
problem: {
problemLt: '',
problemEn: '',
issueId: null,
},
ok: null,
error: null,
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
const response = await authAxios.get(
`/api/Issues/${this.$route.params.id}`
)
this.issue = response.data
const response2 = await axios.get('/api/IssueTypes')
response2.data.forEach((it) => {
this.issueTypes[it.id] = it
})
},
async create() {
try {
this.problem.issueId = this.issue.id
await authAxios.post('/api/PublishedProblems', this.problem)
this.ok = 'Sukurtą.'
} catch (error) {
this.error = error
}
},
},
watch: {
$route: 'fetchData',
},
}
</script>

View File

@@ -0,0 +1,67 @@
<template>
<div class="container">
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
<div class="alert alert-danger" v-if="error">{{ error }}</div>
<div class="card my-4" v-for="p in problems" :key="p.id">
<div class="row g-0">
<div class="col-lg-2 col-md-3">
<div class="card-body">
<span class="h3">{{ votes[p.id] }}</span> aktualumo&nbsp;balas
<button @click="vote(p.id)" class="btn btn-primary">
Balsuoti
</button>
</div>
</div>
<div class="col-lg col-md">
<div class="card-body">
<h6 class="card-text text-muted">Paskelbta: {{ p.created }}</h6>
<h5 class="card-title">Problema:</h5>
<p class="card-text">{{ p.problemLt }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { axios, authAxios } from '@/axios'
export default {
data() {
return {
problems: [],
votes: {},
ok: null,
error: null,
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
const response = await axios.get('/api/PublishedProblems')
this.problems = response.data
this.problems.forEach((p) => {
this.fetchVoteCount(p.id)
})
},
async fetchVoteCount(id) {
const response = await axios.get(`/api/PublishedProblems/${id}/Votes`)
this.votes[id] = response.data
},
async vote(id) {
this.ok = null
this.error = null
try {
await authAxios.post(`/api/PublishedProblems/${id}/Votes`)
this.ok = 'Sekmingai prabalsuotą.'
await this.fetchVoteCount(id)
} catch (error) {
this.error = error
}
},
},
}
</script>

View File

@@ -1,5 +1,7 @@
<template> <template>
<div class="container mt-2"> <div class="container mt-2">
<div class="alert alert-danger" v-if="error">{{ error }}</div>
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="card col-lg-6 p-5"> <div class="card col-lg-6 p-5">
<h1>Pateik savo problemą</h1> <h1>Pateik savo problemą</h1>
@@ -23,9 +25,9 @@
id="issueTypeSelect" id="issueTypeSelect"
class="form-select" class="form-select"
> >
<option v-for="it in issueTypes" :key="it.id" :value="it.id">{{ <option v-for="it in issueTypes" :key="it.id" :value="it.id">
it.name {{ it.name }}
}}</option> </option>
</select> </select>
</div> </div>
<div class="mb-3"> <div class="mb-3">
@@ -53,15 +55,15 @@
studentai. studentai.
</label> </label>
</div> </div>
<button class="btn btn-primary">Pateikti</button> <button @click="submit" class="btn btn-primary btn-lg">Pateikti</button>
</div> </div>
</div> </div>
</div> </div>
<pre>{{ $data }}</pre>
</template> </template>
<script> <script>
import axios from 'axios' import { authAxios, axios } from '@/axios'
export default { export default {
data() { data() {
return { return {
@@ -71,6 +73,8 @@ export default {
issueTypeId: null, issueTypeId: null,
publishable: false, publishable: false,
}, },
error: null,
ok: null,
} }
}, },
created() { created() {
@@ -81,6 +85,20 @@ export default {
const response = await axios.get('/api/IssueTypes') const response = await axios.get('/api/IssueTypes')
this.issueTypes = response.data this.issueTypes = response.data
}, },
async submit() {
this.error = null
this.ok = null
try {
const response = await authAxios.post('/api/Issues', this.issue)
console.log(response)
this.ok = 'Problemą sekmingai pateikta.'
this.issue.description = ''
this.issue.publishable = false
this.issue.issueTypeId = null
} catch (error) {
this.error = error
}
},
}, },
} }
</script> </script>

View File

@@ -1,6 +1,11 @@
import { createWebHistory, createRouter } from 'vue-router' import { createWebHistory, createRouter } from 'vue-router'
import Home from '@/pages/Home.vue' import Home from '@/pages/Home.vue'
import Submit from '@/pages/Submit.vue' import Submit from '@/pages/Submit.vue'
import Problems from '@/pages/Problems.vue'
import Feedbacks from '@/pages/Feedbacks.vue'
import Issues from '@/pages/Issues.vue'
import NewProblem from '@/pages/NewProblem.vue'
import NewFeedback from '@/pages/NewFeedback.vue'
const routes = [ const routes = [
{ {
@@ -13,6 +18,31 @@ const routes = [
name: 'Submit', name: 'Submit',
component: Submit, component: Submit,
}, },
{
path: '/problems',
name: 'Problems',
component: Problems,
},
{
path: '/feedbacks',
name: 'Feedbacks',
component: Feedbacks,
},
{
path: '/issues',
name: 'Issues',
component: Issues,
},
{
path: '/issues/:id/newProblem',
name: 'IssueNewProblem',
component: NewProblem,
},
{
path: '/issues/:id/newFeedback',
name: 'IssueNewFeedback',
component: NewFeedback,
},
] ]
const router = createRouter({ const router = createRouter({

View File

@@ -3,6 +3,7 @@ import axios from 'axios'
// initial state // initial state
const state = () => ({ const state = () => ({
isReady: false,
isLoggedIn: false, isLoggedIn: false,
isAdmin: false, isAdmin: false,
accessToken: null, accessToken: null,
@@ -11,7 +12,8 @@ const state = () => ({
displayName: null, displayName: null,
debugFullTokenResponse: null, debugFullTokenResponse: null,
debugAccountInfo: null, // debugAccountInfo: null,
onReady: [],
}) })
// getters // getters
@@ -30,12 +32,19 @@ const actions = {
Authorization: `Bearer ${state.idToken}`, Authorization: `Bearer ${state.idToken}`,
}, },
}) })
.then(res => (isAdmin = res.data)) .then((res) => (isAdmin = res.data))
.catch(error => console.error(error)) .catch((error) => console.error(error))
} }
commit('setState', { ...state, isAdmin }) commit('setState', { ...state, isAdmin })
}) })
}, },
async waitTillReady({ commit, state }) {
if (!state.isReady) {
await new Promise((c) => {
commit('addReadyCalback', c)
})
}
},
} }
// mutations // mutations
@@ -46,9 +55,16 @@ const mutations = {
state.accessToken = msalState.accessToken state.accessToken = msalState.accessToken
state.idToken = msalState.idToken state.idToken = msalState.idToken
state.debugFullTokenResponse = msalState.debugFullTokenResponse state.debugFullTokenResponse = msalState.debugFullTokenResponse
state.debugAccountInfo = msalState.debugAccountInfo // state.debugAccountInfo = msalState.debugAccountInfo
state.email = msalState.email state.email = msalState.email
state.displayName = msalState.displayName state.displayName = msalState.displayName
if (!state.isReady && state.idToken) {
state.isReady = true
state.onReady.forEach((c) => c())
}
},
addReadyCalback(state, callback) {
state.onReady.push(callback)
}, },
} }

View File

@@ -45,6 +45,11 @@ namespace KTUSAPS.Controllers
[HttpGet("Authed")] [HttpGet("Authed")]
public bool IsAuthed() => true; public bool IsAuthed() => true;
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
[HttpGet("Claims")]
public IEnumerable<object> Claims() => User.Claims.Select((c) => new { c.Type, c.Value });
[Authorize("admin")] [Authorize("admin")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[HttpGet("Admin")] [HttpGet("Admin")]

View File

@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
using KTUSAPS.Data; using KTUSAPS.Data;
using KTUSAPS.Data.Model; using KTUSAPS.Data.Model;
using KTUSAPS.Extensions; using KTUSAPS.Extensions;
using Microsoft.AspNetCore.Authorization;
namespace KTUSAPS.Controllers namespace KTUSAPS.Controllers
{ {
@@ -32,6 +33,7 @@ namespace KTUSAPS.Controllers
[HttpPost] [HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[Authorize("admin")]
public async Task<ActionResult<IssueType>> CreateIssueType([FromBody] IssueType issueType) public async Task<ActionResult<IssueType>> CreateIssueType([FromBody] IssueType issueType)
{ {
if (issueType == null) if (issueType == null)
@@ -64,6 +66,7 @@ namespace KTUSAPS.Controllers
[HttpPatch("{id}")] [HttpPatch("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<IActionResult> UpdateIssueType(int id, IssueType issueType) public async Task<IActionResult> UpdateIssueType(int id, IssueType issueType)
{ {
var databaseIssueType = await _context.IssueTypes.FindAsync(id); var databaseIssueType = await _context.IssueTypes.FindAsync(id);
@@ -86,6 +89,7 @@ namespace KTUSAPS.Controllers
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<IActionResult> DeleteIssueType(int id) public async Task<IActionResult> DeleteIssueType(int id)
{ {
var issueType = await _context.IssueTypes.FindAsync(id); var issueType = await _context.IssueTypes.FindAsync(id);

View File

@@ -1,5 +1,6 @@
using KTUSAPS.Data.Model; using KTUSAPS.Data.Model;
using KTUSAPS.Extensions; using KTUSAPS.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -23,6 +24,7 @@ namespace KTUSAPS.Controllers
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[Authorize("admin")]
public async Task<ActionResult<IEnumerable<Issue>>> GetIssues() public async Task<ActionResult<IEnumerable<Issue>>> GetIssues()
{ {
return await dataContext.Issues.ToListAsync(); return await dataContext.Issues.ToListAsync();
@@ -31,6 +33,7 @@ namespace KTUSAPS.Controllers
[HttpPost] [HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[Authorize]
public async Task<ActionResult<Issue>> CreateIssueAsync([FromBody] Issue issueToCreate) public async Task<ActionResult<Issue>> CreateIssueAsync([FromBody] Issue issueToCreate)
{ {
if (issueToCreate == null) if (issueToCreate == null)
@@ -41,9 +44,11 @@ namespace KTUSAPS.Controllers
return BadRequest("No typeId has been specified"); return BadRequest("No typeId has been specified");
if (issueToCreate.Problem != null && issueToCreate.Feedback != null && issueToCreate.IssueType != null) if (issueToCreate.Problem != null && issueToCreate.Feedback != null && issueToCreate.IssueType != null)
return BadRequest("Do not privide navigation property values."); return BadRequest("Do not privide navigation property values.");
// TODO: Enable next line and make thoes two fields come from user identity if (issueToCreate.UserID != default || issueToCreate.Email != default)
//if (issueToCreate.UserID != default || issueToCreate.Email != default) return BadRequest("Do not provide indentity values.");
// return BadRequest("Do not provide indentity values.");
issueToCreate.UserID = User.GetUserId();
issueToCreate.Email = User.GetEmail();
var createdValue = await dataContext.AddAsync(issueToCreate); var createdValue = await dataContext.AddAsync(issueToCreate);
await dataContext.SaveChangesAsync(); await dataContext.SaveChangesAsync();
@@ -54,6 +59,7 @@ namespace KTUSAPS.Controllers
[HttpGet("{id}")] [HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public ActionResult<Issue> GetIssue(int id) public ActionResult<Issue> GetIssue(int id)
{ {
var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault(); var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
@@ -65,6 +71,7 @@ namespace KTUSAPS.Controllers
[HttpPatch("{id}")] [HttpPatch("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<ActionResult<Issue>> UpdateIssueAsync(int id, [FromBody] Issue issue) public async Task<ActionResult<Issue>> UpdateIssueAsync(int id, [FromBody] Issue issue)
{ {
var databaseIssue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault(); var databaseIssue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
@@ -83,6 +90,7 @@ namespace KTUSAPS.Controllers
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<IActionResult> DeleteIssueAsync(int id) public async Task<IActionResult> DeleteIssueAsync(int id)
{ {
var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault(); var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();

View File

@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
using KTUSAPS.Data; using KTUSAPS.Data;
using KTUSAPS.Data.Model; using KTUSAPS.Data.Model;
using KTUSAPS.Extensions; using KTUSAPS.Extensions;
using Microsoft.AspNetCore.Authorization;
namespace KTUSAPS.Controllers namespace KTUSAPS.Controllers
{ {
@@ -32,6 +33,7 @@ namespace KTUSAPS.Controllers
[HttpPost] [HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[Authorize("admin")]
public async Task<ActionResult<PublishedFeedback>> PostPublishedFeedback(PublishedFeedback publishedFeedback) public async Task<ActionResult<PublishedFeedback>> PostPublishedFeedback(PublishedFeedback publishedFeedback)
{ {
if (publishedFeedback == null) if (publishedFeedback == null)
@@ -63,6 +65,7 @@ namespace KTUSAPS.Controllers
[HttpPatch("{id}")] [HttpPatch("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<ActionResult<PublishedFeedback>> UpdatePublishedFeedback(int id, PublishedFeedback publishedFeedback) public async Task<ActionResult<PublishedFeedback>> UpdatePublishedFeedback(int id, PublishedFeedback publishedFeedback)
{ {
var databasePublishedFeedback = await _context.PublishedFeedbacks.FindAsync(id); var databasePublishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
@@ -88,6 +91,7 @@ namespace KTUSAPS.Controllers
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<IActionResult> DeletePublishedFeedback(int id) public async Task<IActionResult> DeletePublishedFeedback(int id)
{ {
var publishedFeedback = await _context.PublishedFeedbacks.FindAsync(id); var publishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);

View File

@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
using KTUSAPS.Data; using KTUSAPS.Data;
using KTUSAPS.Data.Model; using KTUSAPS.Data.Model;
using KTUSAPS.Extensions; using KTUSAPS.Extensions;
using Microsoft.AspNetCore.Authorization;
namespace KTUSAPS.Controllers namespace KTUSAPS.Controllers
{ {
@@ -32,6 +33,7 @@ namespace KTUSAPS.Controllers
[HttpPost] [HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[Authorize("admin")]
public async Task<ActionResult<PublishedProblem>> CreatePublishedProblem([FromBody] PublishedProblem publishedProblem) public async Task<ActionResult<PublishedProblem>> CreatePublishedProblem([FromBody] PublishedProblem publishedProblem)
{ {
if (publishedProblem == null) if (publishedProblem == null)
@@ -63,6 +65,7 @@ namespace KTUSAPS.Controllers
[HttpPatch("{id}")] [HttpPatch("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<ActionResult<PublishedProblem>> UpdatePublishedProblem(int id, PublishedProblem publishedProblem) public async Task<ActionResult<PublishedProblem>> UpdatePublishedProblem(int id, PublishedProblem publishedProblem)
{ {
var databasePublishedProblem = await _context.PublishedProblems.FindAsync(id); var databasePublishedProblem = await _context.PublishedProblems.FindAsync(id);
@@ -90,6 +93,7 @@ namespace KTUSAPS.Controllers
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize("admin")]
public async Task<IActionResult> DeletePublishedProblem(int id) public async Task<IActionResult> DeletePublishedProblem(int id)
{ {
var publishedProblem = await _context.PublishedProblems.FindAsync(id); var publishedProblem = await _context.PublishedProblems.FindAsync(id);
@@ -121,17 +125,18 @@ namespace KTUSAPS.Controllers
[ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Vote>> Vote(int id, Vote vote) [Authorize]
public async Task<ActionResult<Vote>> Vote(int id)
{ {
var publishedProblem = await _context.PublishedProblems.FindAsync(id); var publishedProblem = await _context.PublishedProblems.FindAsync(id);
if (publishedProblem == null) if (publishedProblem == null)
return NotFound(); return NotFound();
// TODO: Get user id from auth claims var vote = new Vote()
if (vote.UserId == default) {
return BadRequest("Please provide user id"); Problem = publishedProblem,
UserId = User.GetUserId(),
vote.Problem = publishedProblem; };
_context.Votes.Add(vote); _context.Votes.Add(vote);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();

View File

@@ -0,0 +1,41 @@
using System.Linq;
using System.Security.Claims;
namespace KTUSAPS.Extensions
{
public static class ClaimsPrincipalExtensions
{
private static string getClaimValue(ClaimsPrincipal claimsPrincipal, string claimType)
{
return claimsPrincipal.Claims.FirstOrDefault(c => c.Type == claimType)?.Value;
}
public static string GetUserId(this ClaimsPrincipal claimsPrincipal)
{
if (claimsPrincipal == null)
return null;
return getClaimValue(claimsPrincipal, ClaimTypes.NameIdentifier);
}
public static string GetName(this ClaimsPrincipal claimsPrincipal)
{
if (claimsPrincipal == null)
return null;
return getClaimValue(claimsPrincipal, "name");
}
public static string GetEmail(this ClaimsPrincipal claimsPrincipal)
{
if (claimsPrincipal == null)
return null;
return getClaimValue(claimsPrincipal, ClaimTypes.Email);
}
public static string GetObjectId(this ClaimsPrincipal claimsPrincipal)
{
if (claimsPrincipal == null)
return null;
return getClaimValue(claimsPrincipal, "http://schemas.microsoft.com/identity/claims/objectidentifier");
}
}
}