2019-11-13 21:40:14 +00:00
|
|
|
import Vue from 'vue';
|
|
|
|
import Vuex from 'vuex';
|
2019-12-19 23:19:38 +00:00
|
|
|
import AxiosBootstrap from 'axios';
|
2019-12-29 16:04:03 +00:00
|
|
|
import * as _ from 'lodash/fp';
|
2019-12-05 03:32:33 +00:00
|
|
|
import router from '../router';
|
2019-11-13 21:40:14 +00:00
|
|
|
|
2019-12-28 03:05:58 +00:00
|
|
|
import * as base64 from 'base-64';
|
|
|
|
import * as utf8 from 'utf8';
|
2024-01-02 16:46:34 +00:00
|
|
|
import {ticketStateColorLookup, ticketStateIconLookup} from "@/utils";
|
2019-12-28 03:05:58 +00:00
|
|
|
|
2019-11-13 21:40:14 +00:00
|
|
|
Vue.use(Vuex);
|
2019-12-19 23:19:38 +00:00
|
|
|
const axios = AxiosBootstrap.create({
|
2024-01-07 20:38:25 +00:00
|
|
|
baseURL: '/api',
|
2019-12-19 23:19:38 +00:00
|
|
|
});
|
2019-12-19 23:31:28 +00:00
|
|
|
axios.interceptors.response.use(response => response, error => {
|
2024-01-07 20:38:25 +00:00
|
|
|
if (error.response.status === 401) {
|
|
|
|
console.log('401 interceptor fired');
|
|
|
|
store.dispatch('reloadToken').then((ok) => {
|
|
|
|
if (ok) {
|
|
|
|
error.config.headers['Authorization'] = `Token ${store.state.token}`;
|
|
|
|
return axios.request(error.config);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (error.response.status === 403) {
|
2023-11-27 00:14:52 +00:00
|
|
|
const message = `
|
2024-01-07 20:38:25 +00:00
|
|
|
<h3>Access denied.</h3>
|
|
|
|
<p>
|
|
|
|
url: ${error.config.url}
|
|
|
|
<br>
|
|
|
|
method: ${error.config.method}
|
|
|
|
<br>
|
|
|
|
response-body: ${error.response && error.response.body}
|
|
|
|
</p>
|
|
|
|
`;
|
|
|
|
store.commit('createToast', {title: 'Error: Access denied', message, color: 'danger'});
|
|
|
|
return Promise.reject(error)
|
|
|
|
} else {
|
2024-01-13 00:40:37 +00:00
|
|
|
console.error('error interceptor fired', error.message);
|
2024-01-07 20:38:25 +00:00
|
|
|
|
|
|
|
if (error.isAxiosError) {
|
|
|
|
const message = `
|
2019-12-27 01:06:04 +00:00
|
|
|
<h3>A HTTP ${error.config.method} request failed.</h3>
|
2019-12-28 03:24:53 +00:00
|
|
|
<p>
|
|
|
|
url: ${error.config.url}
|
|
|
|
<br>
|
|
|
|
timeout: ${!!error.request.timeout}
|
|
|
|
<br>
|
|
|
|
response-body: ${error.response && error.response.body}
|
|
|
|
</p>
|
2019-12-27 01:06:04 +00:00
|
|
|
`;
|
2024-01-07 20:38:25 +00:00
|
|
|
store.commit('createToast', {title: 'Error: HTTP', message, color: 'danger'});
|
|
|
|
} else {
|
|
|
|
store.commit('createToast', {title: 'Error: Unknown', message: error.toString(), color: 'danger'});
|
|
|
|
}
|
|
|
|
return Promise.reject(error);
|
2023-11-27 00:14:52 +00:00
|
|
|
}
|
2019-12-19 23:31:28 +00:00
|
|
|
});
|
|
|
|
|
2019-12-05 03:32:33 +00:00
|
|
|
const store = new Vuex.Store({
|
2023-11-27 00:14:52 +00:00
|
|
|
state: {
|
|
|
|
keyIncrement: 0,
|
|
|
|
events: [],
|
|
|
|
layout: 'cards',
|
|
|
|
loadedItems: [],
|
2024-01-07 20:38:25 +00:00
|
|
|
itemCache: {},
|
2023-11-27 00:14:52 +00:00
|
|
|
loadedBoxes: [],
|
|
|
|
toasts: [],
|
2024-01-07 20:38:25 +00:00
|
|
|
tickets: [],
|
|
|
|
users: [],
|
|
|
|
groups: [],
|
2024-01-02 16:46:34 +00:00
|
|
|
lastEvent: localStorage.getItem('lf_lastEvent') || '37C3',
|
2024-01-07 20:38:25 +00:00
|
|
|
lastUsed: JSON.parse(localStorage.getItem('lf_lastUsed') || '{}'),
|
|
|
|
remember: false,
|
|
|
|
user: null,
|
|
|
|
password: null,
|
|
|
|
userPermissions: [],
|
|
|
|
token: null,
|
2023-12-28 20:08:26 +00:00
|
|
|
state_options: [],
|
2024-01-07 20:38:25 +00:00
|
|
|
token_expiry: null,
|
|
|
|
local_loaded: false,
|
2023-12-28 21:49:55 +00:00
|
|
|
showAddBoxModal: false,
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
getters: {
|
2024-01-07 20:38:25 +00:00
|
|
|
getEventSlug: state => state.route && state.route.params.event ? state.route.params.event : state.lastEvent,
|
2023-11-27 00:14:52 +00:00
|
|
|
getActiveView: state => state.route.name || 'items',
|
|
|
|
getFilters: state => state.route.query,
|
2024-01-07 20:38:25 +00:00
|
|
|
getBoxes: state => state.loadedBoxes,
|
|
|
|
checkPermission: state => (event, perm) => state.userPermissions.includes(`${event}:${perm}`) || state.userPermissions.includes(`*:${perm}`),
|
|
|
|
hasPermissions: state => state.userPermissions.length > 0,
|
2024-01-02 16:46:34 +00:00
|
|
|
stateInfo: state => (slug) => {
|
|
|
|
const obj = state.state_options.filter((s) => s.value === slug)[0];
|
|
|
|
if (obj) {
|
|
|
|
return {
|
|
|
|
color: ticketStateColorLookup(obj.value),
|
|
|
|
icon: ticketStateIconLookup(obj.value),
|
|
|
|
slug: obj.value,
|
|
|
|
text: obj.text,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
color: 'danger',
|
|
|
|
icon: 'exclamation',
|
|
|
|
slug: slug,
|
|
|
|
text: 'Unknown'
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
2024-01-07 20:38:25 +00:00
|
|
|
isLoggedIn(state) {
|
|
|
|
if (!state.local_loaded) {
|
2024-01-07 22:03:22 +00:00
|
|
|
state.remember = localStorage.getItem('remember') === 'true';
|
|
|
|
state.user = localStorage.getItem('user');
|
|
|
|
//state.password = localStorage.getItem('password');
|
|
|
|
state.userPermissions = JSON.parse(localStorage.getItem('permissions') || '[]');
|
|
|
|
state.token = localStorage.getItem('token');
|
|
|
|
state.token_expiry = localStorage.getItem('token_expiry');
|
|
|
|
state.local_loaded = true;
|
|
|
|
axios.defaults.headers.common['Authorization'] = `Token ${state.token}`;
|
2024-01-07 20:38:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return state.user !== null && state.token !== null;
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
mutations: {
|
|
|
|
updateLastUsed(state, diff) {
|
|
|
|
state.lastUsed = _.extend(state.lastUsed, diff);
|
2024-01-07 20:38:25 +00:00
|
|
|
localStorage.setItem('lf_lastUsed', JSON.stringify(state.lastUsed));
|
|
|
|
},
|
|
|
|
updateLastEvent(state, slug) {
|
|
|
|
state.lastEvent = slug;
|
|
|
|
localStorage.setItem('lf_lastEvent', slug);
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
replaceEvents(state, events) {
|
|
|
|
state.events = events;
|
|
|
|
},
|
2023-12-28 20:08:26 +00:00
|
|
|
replaceTicketStates(state, states) {
|
|
|
|
state.state_options = states;
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
changeView(state, {view, slug}) {
|
|
|
|
router.push({path: `/${slug}/${view}`});
|
|
|
|
},
|
|
|
|
replaceLoadedItems(state, newItems) {
|
|
|
|
state.loadedItems = newItems;
|
|
|
|
},
|
2024-01-07 20:38:25 +00:00
|
|
|
setItemCache(state, {slug, items}) {
|
|
|
|
state.itemCache[slug] = items;
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
setLayout(state, layout) {
|
|
|
|
state.layout = layout;
|
|
|
|
},
|
|
|
|
replaceBoxes(state, loadedBoxes) {
|
|
|
|
state.loadedBoxes = loadedBoxes;
|
|
|
|
},
|
|
|
|
updateItem(state, updatedItem) {
|
|
|
|
const item = state.loadedItems.filter(({uid}) => uid === updatedItem.uid)[0];
|
|
|
|
Object.assign(item, updatedItem);
|
|
|
|
},
|
|
|
|
removeItem(state, item) {
|
|
|
|
state.loadedItems = state.loadedItems.filter(it => it !== item);
|
|
|
|
},
|
|
|
|
appendItem(state, item) {
|
|
|
|
state.loadedItems.push(item);
|
|
|
|
},
|
2024-01-07 20:38:25 +00:00
|
|
|
replaceTickets(state, tickets) {
|
|
|
|
state.tickets = tickets;
|
|
|
|
},
|
|
|
|
replaceUsers(state, users) {
|
|
|
|
state.users = users;
|
|
|
|
},
|
|
|
|
replaceGroups(state, groups) {
|
|
|
|
state.groups = groups;
|
|
|
|
},
|
|
|
|
updateTicket(state, updatedTicket) {
|
|
|
|
const ticket = state.tickets.filter(({id}) => id === updatedTicket.id)[0];
|
|
|
|
Object.assign(ticket, updatedTicket);
|
|
|
|
},
|
2023-12-28 21:49:55 +00:00
|
|
|
openAddBoxModal(state) {
|
|
|
|
state.showAddBoxModal = true;
|
|
|
|
},
|
|
|
|
closeAddBoxModal(state) {
|
|
|
|
state.showAddBoxModal = false;
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
createToast(state, {title, message, color}) {
|
2023-11-27 00:38:43 +00:00
|
|
|
var toast = {title, message, color, key: state.keyIncrement}
|
|
|
|
state.toasts.push(toast);
|
2023-11-27 00:14:52 +00:00
|
|
|
state.keyIncrement += 1;
|
2023-11-27 00:38:43 +00:00
|
|
|
return toast;
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
removeToast(state, key) {
|
|
|
|
state.toasts = state.toasts.filter(toast => toast.key !== key);
|
2024-01-07 20:38:25 +00:00
|
|
|
},
|
|
|
|
setRemember(state, remember) {
|
|
|
|
state.remember = remember;
|
|
|
|
localStorage.setItem('remember', remember);
|
|
|
|
},
|
|
|
|
setUser(state, user) {
|
|
|
|
state.user = user;
|
|
|
|
if (user)
|
|
|
|
localStorage.setItem('user', user);
|
|
|
|
},
|
|
|
|
setPassword(state, password) {
|
|
|
|
state.password = password;
|
|
|
|
},
|
|
|
|
setPermissions(state, permissions) {
|
|
|
|
state.userPermissions = permissions;
|
|
|
|
if (permissions)
|
|
|
|
localStorage.setItem('permissions', JSON.stringify(permissions));
|
|
|
|
},
|
|
|
|
setToken(state, {token, expiry}) {
|
|
|
|
state.token = token;
|
|
|
|
state.token_expiry = expiry;
|
|
|
|
if (token)
|
|
|
|
localStorage.setItem('token', token);
|
|
|
|
localStorage.setItem('token_expiry', expiry);
|
|
|
|
},
|
|
|
|
logout(state) {
|
|
|
|
state.user = null;
|
|
|
|
state.token = null;
|
|
|
|
localStorage.removeItem('user');
|
|
|
|
localStorage.removeItem('permissions');
|
|
|
|
localStorage.removeItem('token');
|
|
|
|
localStorage.removeItem('token_expiry');
|
|
|
|
if (router.currentRoute.name !== 'login')
|
|
|
|
router.push('/login');
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
actions: {
|
2024-01-07 20:38:25 +00:00
|
|
|
async login({commit, dispatch, state}, {username, password, remember}) {
|
|
|
|
commit('setRemember', remember);
|
|
|
|
try {
|
|
|
|
const data = await fetch('/api/2/login/', {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
body: JSON.stringify({username: username, password: password}),
|
|
|
|
credentials: 'omit'
|
|
|
|
}).then(r => r.json())
|
2024-01-07 22:03:22 +00:00
|
|
|
if (data && data.token) {
|
2024-01-07 20:38:25 +00:00
|
|
|
commit('setToken', data);
|
|
|
|
commit('setUser', username);
|
|
|
|
commit('setPassword', password);
|
|
|
|
axios.defaults.headers.common['Authorization'] = `Token ${data.token}`;
|
|
|
|
dispatch('afterLogin');
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
async reloadToken({commit, state}) {
|
|
|
|
try {
|
2024-01-07 22:03:22 +00:00
|
|
|
if (state.password) {
|
2024-01-07 20:38:25 +00:00
|
|
|
const data = await fetch('/api/2/login/', {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
body: JSON.stringify({username: state.user, password: state.password}),
|
|
|
|
credentials: 'omit'
|
2024-01-07 22:03:22 +00:00
|
|
|
}).then(r => r.json()).catch(e => console.error(e))
|
|
|
|
if (data && data.token) {
|
2024-01-07 20:38:25 +00:00
|
|
|
commit('setToken', data);
|
|
|
|
axios.defaults.headers.common['Authorization'] = `Token ${data.token}`;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
//credentials failed, logout
|
|
|
|
store.commit('logout');
|
|
|
|
},
|
2024-01-13 00:40:37 +00:00
|
|
|
//async verifyToken({commit, state}) {
|
2024-01-07 20:38:25 +00:00
|
|
|
async afterLogin({dispatch}) {
|
|
|
|
const boxes = dispatch('loadBoxes');
|
|
|
|
const items = dispatch('loadEventItems');
|
|
|
|
const tickets = dispatch('loadTickets');
|
|
|
|
const user = dispatch('loadUserInfo');
|
|
|
|
await Promise.all([boxes, items, tickets, user]);
|
|
|
|
},
|
|
|
|
async fetchImage({state}, url) {
|
|
|
|
return await fetch(url, {headers: {'Authorization': `Token ${state.token}`}});
|
|
|
|
},
|
|
|
|
async loadUserInfo({commit}) {
|
|
|
|
const {data} = await axios.get('/2/self/');
|
|
|
|
commit('setUser', data.username);
|
|
|
|
commit('setPermissions', data.permissions);
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
async loadEvents({commit}) {
|
2024-01-07 20:38:25 +00:00
|
|
|
const {data} = await axios.get('/2/events/');
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('replaceEvents', data);
|
|
|
|
},
|
2023-12-28 20:08:26 +00:00
|
|
|
async fetchTicketStates({commit}) {
|
|
|
|
const {data} = await axios.get('/2/tickets/states/');
|
|
|
|
commit('replaceTicketStates', data);
|
|
|
|
},
|
2024-01-07 20:38:25 +00:00
|
|
|
changeEvent({dispatch, getters, commit}, eventName) {
|
|
|
|
router.push({path: `/${eventName.slug}/${getters.getActiveView}/`});
|
2023-11-27 00:14:52 +00:00
|
|
|
dispatch('loadEventItems');
|
|
|
|
},
|
|
|
|
changeView({getters}, link) {
|
2024-01-07 20:38:25 +00:00
|
|
|
router.push({path: `/${getters.getEventSlug}/${link.path}/`});
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
showBoxContent({getters}, box) {
|
2024-01-07 20:38:25 +00:00
|
|
|
router.push({path: `/${getters.getEventSlug}/items/`, query: {box}});
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
2024-01-07 20:38:25 +00:00
|
|
|
async loadEventItems({commit, getters, state}) {
|
|
|
|
try {
|
|
|
|
commit('replaceLoadedItems', []);
|
|
|
|
const slug = getters.getEventSlug;
|
|
|
|
if (slug in state.itemCache) {
|
|
|
|
commit('replaceLoadedItems', state.itemCache[slug]);
|
|
|
|
}
|
|
|
|
const {data} = await axios.get(`/2/${slug}/items/`);
|
|
|
|
commit('replaceLoadedItems', data);
|
|
|
|
commit('setItemCache', {slug, items: data});
|
|
|
|
} catch (e) {
|
|
|
|
console.error("Error loading items");
|
|
|
|
}
|
2023-11-27 00:14:52 +00:00
|
|
|
},
|
|
|
|
async searchEventItems({commit, getters}, query) {
|
|
|
|
const foo = utf8.encode(query);
|
|
|
|
const bar = base64.encode(foo);
|
2019-12-28 03:05:58 +00:00
|
|
|
|
2024-01-07 20:38:25 +00:00
|
|
|
const {data} = await axios.get(`/2/${getters.getEventSlug}/items/${bar}/`);
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('replaceLoadedItems', data);
|
|
|
|
},
|
|
|
|
async loadBoxes({commit}) {
|
2024-01-07 20:38:25 +00:00
|
|
|
const {data} = await axios.get('/2/boxes/');
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('replaceBoxes', data);
|
|
|
|
},
|
2023-12-28 21:49:55 +00:00
|
|
|
async createBox({commit, dispatch}, box) {
|
|
|
|
const {data} = await axios.post('/2/boxes/', box);
|
|
|
|
commit('replaceBoxes', data);
|
|
|
|
dispatch('loadBoxes').then(() => {
|
|
|
|
commit('closeAddBoxModal');
|
|
|
|
});
|
|
|
|
},
|
2023-12-29 17:22:48 +00:00
|
|
|
async deleteBox({commit, dispatch}, box_id) {
|
|
|
|
await axios.delete(`/2/boxes/${box_id}/`);
|
|
|
|
dispatch('loadBoxes');
|
|
|
|
},
|
2023-11-27 00:14:52 +00:00
|
|
|
async updateItem({commit, getters}, item) {
|
2024-01-07 20:38:25 +00:00
|
|
|
const {data} = await axios.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item);
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('updateItem', data);
|
|
|
|
},
|
|
|
|
async markItemReturned({commit, getters}, item) {
|
2024-01-07 20:38:25 +00:00
|
|
|
await axios.patch(`/2/${getters.getEventSlug}/item/${item.uid}/`, {returned: true});
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('removeItem', item);
|
|
|
|
},
|
|
|
|
async deleteItem({commit, getters}, item) {
|
2024-01-07 20:38:25 +00:00
|
|
|
await axios.delete(`/2/${getters.getEventSlug}/item/${item.uid}/`, item);
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('removeItem', item);
|
|
|
|
},
|
|
|
|
async postItem({commit, getters}, item) {
|
|
|
|
commit('updateLastUsed', {box: item.box, cid: item.cid});
|
2024-01-07 20:38:25 +00:00
|
|
|
const {data} = await axios.post(`/2/${getters.getEventSlug}/item/`, item);
|
2023-11-27 00:14:52 +00:00
|
|
|
commit('appendItem', data);
|
2024-01-07 20:38:25 +00:00
|
|
|
},
|
|
|
|
async loadTickets({commit}) {
|
|
|
|
const {data} = await axios.get('/2/tickets/');
|
|
|
|
commit('replaceTickets', data);
|
|
|
|
},
|
|
|
|
async sendMail({commit, dispatch}, {id, message}) {
|
|
|
|
const {data} = await axios.post(`/2/tickets/${id}/reply/`, {message});
|
|
|
|
await dispatch('loadTickets');
|
|
|
|
},
|
|
|
|
async postManualTicket({commit, dispatch}, {sender, message, title,}) {
|
2023-12-28 20:08:26 +00:00
|
|
|
const {data} = await axios.post(`/2/tickets/manual/`, {
|
|
|
|
name: title,
|
|
|
|
sender,
|
|
|
|
body: message,
|
|
|
|
recipient: 'mail@c3lf.de'
|
|
|
|
});
|
2024-01-07 20:38:25 +00:00
|
|
|
await dispatch('loadTickets');
|
|
|
|
},
|
2024-01-13 00:40:37 +00:00
|
|
|
async postComment({commit, dispatch}, {id, message}) {
|
|
|
|
const {data} = await axios.post(`/2/tickets/${id}/comment/`, {comment: message});
|
|
|
|
await dispatch('loadTickets');
|
|
|
|
},
|
2024-01-07 20:38:25 +00:00
|
|
|
async loadUsers({commit}) {
|
|
|
|
const {data} = await axios.get('/2/users/');
|
|
|
|
commit('replaceUsers', data);
|
|
|
|
},
|
|
|
|
async loadGroups({commit}) {
|
|
|
|
const {data} = await axios.get('/2/groups/');
|
|
|
|
commit('replaceGroups', data);
|
|
|
|
},
|
2023-12-28 20:08:26 +00:00
|
|
|
async updateTicket({commit}, ticket) {
|
|
|
|
const {data} = await axios.put(`/2/tickets/${ticket.id}/`, ticket);
|
|
|
|
commit('updateTicket', data);
|
|
|
|
},
|
|
|
|
async updateTicketPartial({commit}, {id, ...ticket}) {
|
|
|
|
const {data} = await axios.patch(`/2/tickets/${id}/`, ticket);
|
|
|
|
commit('updateTicket', data);
|
|
|
|
}
|
2019-12-14 22:51:06 +00:00
|
|
|
}
|
2019-11-13 21:40:14 +00:00
|
|
|
});
|
2019-12-05 03:32:33 +00:00
|
|
|
|
|
|
|
export default store;
|
|
|
|
|
2023-11-27 00:14:52 +00:00
|
|
|
store.dispatch('loadEvents').then(() => {
|
2024-01-07 20:38:25 +00:00
|
|
|
if (store.getters.isLoggedIn) {
|
|
|
|
axios.defaults.headers.common['Authorization'] = `Token ${store.state.token}`;
|
|
|
|
store.dispatch('afterLogin');
|
|
|
|
}
|
2019-12-27 01:06:04 +00:00
|
|
|
});
|