diff options
Diffstat (limited to 'src/site/store')
| -rw-r--r-- | src/site/store/.eslintrc.json | 5 | ||||
| -rw-r--r-- | src/site/store/admin.js | 122 | ||||
| -rw-r--r-- | src/site/store/albums.js | 136 | ||||
| -rw-r--r-- | src/site/store/alert.js | 33 | ||||
| -rw-r--r-- | src/site/store/auth.js | 106 | ||||
| -rw-r--r-- | src/site/store/config.js | 18 | ||||
| -rw-r--r-- | src/site/store/images.js | 193 | ||||
| -rw-r--r-- | src/site/store/index.js | 63 | ||||
| -rw-r--r-- | src/site/store/tags.js | 40 |
9 files changed, 662 insertions, 54 deletions
diff --git a/src/site/store/.eslintrc.json b/src/site/store/.eslintrc.json new file mode 100644 index 0000000..052e3ef --- /dev/null +++ b/src/site/store/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-shadow": ["error", { "allow": ["state"] }] + } +} diff --git a/src/site/store/admin.js b/src/site/store/admin.js new file mode 100644 index 0000000..58b63b5 --- /dev/null +++ b/src/site/store/admin.js @@ -0,0 +1,122 @@ +export const state = () => ({ + users: [], + user: { + id: null, + username: null, + enabled: false, + createdAt: null, + editedAt: null, + apiKeyEditedAt: null, + isAdmin: null, + files: [] + }, + file: {}, + settings: {} +}); + +export const actions = { + async fetchSettings({ commit }) { + const response = await this.$axios.$get('service/config'); + commit('setSettings', response); + + return response; + }, + async fetchUsers({ commit }) { + const response = await this.$axios.$get('admin/users'); + commit('setUsers', response); + + return response; + }, + async fetchUser({ commit }, id) { + const response = await this.$axios.$get(`admin/users/${id}`); + commit('setUserInfo', response); + + return response; + }, + async fetchFile({ commit }, id) { + const response = await this.$axios.$get(`admin/file/${id}`); + commit('setFile', response); + commit('setUserInfo', response); + + return response; + }, + async banIP(_, ip) { + const response = await this.$axios.$post('admin/ban/ip', { ip }); + + return response; + }, + async enableUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/enable', { id }); + + commit('changeUserState', { userId: id, enabled: true }); + + return response; + }, + async disableUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/disable', { id }); + + commit('changeUserState', { userId: id, enabled: false }); + + return response; + }, + async promoteUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/promote', { id }); + + commit('changeUserState', { userId: id, isAdmin: true }); + + return response; + }, + async demoteUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/demote', { id }); + + commit('changeUserState', { userId: id, isAdmin: false }); + + return response; + }, + async purgeUserFiles(_, id) { + const response = await this.$axios.$post('admin/users/purge', { id }); + + return response; + }, + async restartService() { + const response = await this.$axios.$post('service/restart'); + + return response; + } +}; + +export const mutations = { + setSettings(state, { config }) { + state.settings = config; + }, + setUsers(state, { users }) { + state.users = users; + }, + setUserInfo(state, { user, files }) { + state.user = { ...state.user, ...user }; + state.user.files = files || []; + }, + setFile(state, { file }) { + state.file = file || {}; + }, + changeUserState(state, { userId, enabled, isAdmin }) { + const foundIndex = state.users.findIndex(({ id }) => id === userId); + if (foundIndex > -1) { + if (enabled !== undefined) { + state.users[foundIndex].enabled = enabled; + } + if (isAdmin !== undefined) { + state.users[foundIndex].isAdmin = isAdmin; + } + } + + if (state.user.id === userId) { + if (enabled !== undefined) { + state.user.enabled = enabled; + } + if (isAdmin !== undefined) { + state.user.isAdmin = isAdmin; + } + } + } +}; diff --git a/src/site/store/albums.js b/src/site/store/albums.js new file mode 100644 index 0000000..4f796a1 --- /dev/null +++ b/src/site/store/albums.js @@ -0,0 +1,136 @@ +import Vue from 'vue'; + +export const state = () => ({ + list: [], + isListLoading: false, + albumDetails: {}, + expandedAlbums: [], + tinyDetails: [] +}); + +export const getters = { + isExpanded: (state) => (id) => state.expandedAlbums.indexOf(id) > -1, + getDetails: (state) => (id) => state.albumDetails[id] || {} +}; + +export const actions = { + async fetch({ commit }) { + commit('albumsRequest'); + const response = await this.$axios.$get('albums/mini'); + + commit('setAlbums', response.albums); + + return response; + }, + async fetchDetails({ commit }, albumId) { + const response = await this.$axios.$get(`album/${albumId}/links`); + + commit('setDetails', { + id: albumId, + details: { + links: response.links + } + }); + + return response; + }, + async createAlbum({ commit }, name) { + const response = await this.$axios.$post('album/new', { name }); + + commit('addAlbum', response.data); + + return response; + }, + async deleteAlbum({ commit }, albumId) { + const response = await this.$axios.$delete(`album/${albumId}`); + + commit('removeAlbum', albumId); + + return response; + }, + async createLink({ commit }, albumId) { + const response = await this.$axios.$post('album/link/new', { albumId }); + + commit('addAlbumLink', { albumId, data: response.data }); + + return response; + }, + async createCustomLink({ commit }, { albumId, value }) { + const response = await this.$axios.$post('album/link/new', { albumId, identifier: value }); + + commit('addAlbumLink', { albumId, data: response.data }); + + return response; + }, + async updateLinkOptions({ commit }, { albumId, linkOpts }) { + const response = await this.$axios.$post('album/link/edit', { + identifier: linkOpts.identifier, + enableDownload: linkOpts.enableDownload, + enabled: linkOpts.enabled + }); + + commit('updateAlbumLinkOpts', { albumId, linkOpts: response.data }); + + return response; + }, + async deleteLink({ commit }, { albumId, identifier }) { + const response = await this.$axios.$delete(`album/link/delete/${identifier}`); + + commit('removeAlbumLink', { albumId, identifier }); + + return response; + }, + async getTinyDetails({ commit }) { + const response = await this.$axios.$get('albums/dropdown'); + + commit('setTinyDetails', response); + + return response; + } +}; + +export const mutations = { + albumsRequest(state) { + state.isLoading = true; + }, + setAlbums(state, albums) { + state.list = albums; + state.isLoading = false; + }, + addAlbum(state, album) { + state.list.unshift(album); + }, + removeAlbum(state, albumId) { + // state.list = state.list.filter(({ id }) => id !== albumId); + const foundIndex = state.list.findIndex(({ id }) => id === albumId); + state.list.splice(foundIndex, 1); + }, + setDetails(state, { id, details }) { + Vue.set(state.albumDetails, id, details); + }, + addAlbumLink(state, { albumId, data }) { + state.albumDetails[albumId].links.push(data); + }, + updateAlbumLinkOpts(state, { albumId, linkOpts }) { + const foundIndex = state.albumDetails[albumId].links.findIndex( + ({ identifier }) => identifier === linkOpts.identifier + ); + const link = state.albumDetails[albumId].links[foundIndex]; + state.albumDetails[albumId].links[foundIndex] = { ...link, ...linkOpts }; + }, + removeAlbumLink(state, { albumId, identifier }) { + const foundIndex = state.albumDetails[albumId].links.findIndex(({ identifier: id }) => id === identifier); + if (foundIndex > -1) state.albumDetails[albumId].links.splice(foundIndex, 1); + }, + toggleExpandedState(state, id) { + const foundIndex = state.expandedAlbums.indexOf(id); + if (foundIndex > -1) { + state.expandedAlbums.splice(foundIndex, 1); + } else { + state.expandedAlbums.push(id); + } + }, + setTinyDetails(state, { albums }) { + state.tinyDetails = albums; + } +}; diff --git a/src/site/store/alert.js b/src/site/store/alert.js new file mode 100644 index 0000000..cbd6359 --- /dev/null +++ b/src/site/store/alert.js @@ -0,0 +1,33 @@ +import AlertTypes from '~/constants/alertTypes'; + +const getDefaultState = () => ({ + message: null, + type: null, + snackbar: false +}); + +export const state = getDefaultState; + +export const actions = { + set({ commit }, data) { + // Only exists for backwards compatibility, remove one day + if (data.error === true) data.type = AlertTypes.ERROR; + if (data.text !== undefined) data.message = data.text; + + commit('set', data); + }, + clear({ commit }) { + commit('clear'); + } +}; + +export const mutations = { + set(state, { message, type, snackbar }) { + state.message = message; + state.type = type; + state.snackbar = snackbar || false; + }, + clear(state) { + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/auth.js b/src/site/store/auth.js new file mode 100644 index 0000000..85e3a39 --- /dev/null +++ b/src/site/store/auth.js @@ -0,0 +1,106 @@ +const getDefaultState = () => ({ + loggedIn: false, + user: { + id: null, + isAdmin: false, + username: null + }, + token: null +}); + +export const state = getDefaultState; + +export const getters = { + isLoggedIn: (state) => state.loggedIn, + getApiKey: (state) => state.user?.apiKey, + getToken: (state) => state.token +}; + +export const actions = { + async verify({ commit, dispatch }) { + try { + const response = await this.$axios.$get('verify'); + commit('loginSuccess', response); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + async login({ commit }, { username, password }) { + commit('loginRequest'); + + const data = await this.$axios.$post('auth/login', { username, password }); + this.$axios.setToken(data.token, 'Bearer'); + + commit('setToken', data.token); + commit('loginSuccess', { token: data.token, user: data.user }); + }, + async register(_, { username, password }) { + return this.$axios.$post('auth/register', { + username, + password + }); + }, + async fetchCurrentUser({ commit, dispatch }) { + try { + const data = await this.$axios.$get('users/me'); + commit('setUser', data.user); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + async changePassword({ dispatch }, { password, newPassword }) { + try { + const response = await this.$axios.$post('user/password/change', { + password, + newPassword + }); + + return response; + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + + return null; + }, + async requestAPIKey({ commit, dispatch }) { + try { + const response = await this.$axios.$post('user/apikey/change'); + commit('setApiKey', response.apiKey); + + return response; + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + + return null; + }, + logout({ commit }) { + commit('logout'); + } +}; + +export const mutations = { + setToken(state, token) { + state.token = token; + }, + setApiKey(state, apiKey) { + state.user.apiKey = apiKey; + }, + setUser(state, user) { + state.user = user; + }, + loginRequest(state) { + state.isLoading = true; + }, + loginSuccess(state, { user }) { + this.$cookies.set('token', state.token, { path: '/' }); + state.user = user; + state.loggedIn = true; + state.isLoading = false; + }, + logout(state) { + this.$cookies.remove('token', { path: '/' }); + // reset state to default + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/config.js b/src/site/store/config.js new file mode 100644 index 0000000..c17632d --- /dev/null +++ b/src/site/store/config.js @@ -0,0 +1,18 @@ +export const state = () => ({ + development: true, + version: '4.0.0', + URL: 'http://localhost:8080', + baseURL: 'http://localhost:8080/api', + serviceName: '', + maxFileSize: 100, + chunkSize: 90, + maxLinksPerAlbum: 5, + publicMode: false, + userAccounts: false +}); + +export const mutations = { + set(state, config) { + Object.assign(state, config); + } +}; diff --git a/src/site/store/images.js b/src/site/store/images.js new file mode 100644 index 0000000..69b4f5e --- /dev/null +++ b/src/site/store/images.js @@ -0,0 +1,193 @@ +import Vue from 'vue'; + +export const getDefaultState = () => ({ + files: [], + isLoading: false, + pagination: { + page: 1, + limit: 30, + totalFiles: 0 + }, + search: '', + showList: false, + albumName: null, + albumDownloadEnabled: false, + fileExtraInfoMap: {}, // information about the selected file + fileAlbumsMap: {}, // map of file ids with a list of album objects the file is in + fileTagsMap: {} // map of file ids with a list of tag objects for the file +}); + +export const state = getDefaultState; + +export const getters = { + getTotalFiles: ({ pagination }) => pagination.totalFiles, + getFetchedCount: ({ files }) => files.length, + shouldPaginate: ({ pagination }) => pagination.totalFiles > pagination.limit, + getLimit: ({ pagination }) => pagination.limit, + getName: ({ name }) => name +}; + +export const actions = { + async fetch({ commit, dispatch, state }, page) { + commit('setIsLoading'); + + page = page || 1; + + try { + const response = await this.$axios.$get('files', { params: { limit: state.pagination.limit, page } }); + + commit('setFilesAndMeta', { ...response, page }); + + return response; + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + + return null; + }, + async fetchByAlbumId({ commit, state }, { id, page }) { + commit('setIsLoading'); + + page = page || 1; + + const response = await this.$axios.$get(`album/${id}/full`, { + params: { limit: state.pagination.limit, page } + }); + + commit('setFilesAndMeta', { ...response, page }); + + return response; + }, + async fetchFileMeta({ commit }, fileId) { + const response = await this.$axios.$get(`file/${fileId}`); + + commit('setFileAlbums', { ...response, fileId }); + commit('setFileTags', { ...response, fileId }); + commit('setFileExtraInfo', { ...response, fileId }); + + return response; + }, + async getFileAlbums({ commit }, fileId) { + const response = await this.$axios.$get(`file/${fileId}/albums`); + + commit('setFileAlbums', { ...response, fileId }); + + return response; + }, + async addToAlbum({ commit }, { fileId, albumId }) { + const response = await this.$axios.$post('file/album/add', { fileId, albumId }); + + commit('addAlbumToFile', { fileId, albumId, ...response.data }); + + return response; + }, + async removeFromAlbum({ commit }, { fileId, albumId }) { + const response = await this.$axios.$post('file/album/del', { fileId, albumId }); + + commit('removeAlbumFromFile', { fileId, albumId }); + + return response; + }, + async deleteFile({ commit }, fileId) { + const response = await this.$axios.$delete(`file/${fileId}`); + + commit('removeFile', fileId); + + return response; + }, + async addTag({ commit }, { fileId, tagName }) { + const response = await this.$axios.$post('file/tag/add', { fileId, tagName }); + + commit('addTagToFile', response.data); + + return response; + }, + async removeTag({ commit }, { fileId, tagName }) { + const response = await this.$axios.$post('file/tag/del', { fileId, tagName }); + + commit('removeTagFromFile', response.data); + + return response; + }, + async search({ commit, dispatch }, { q, albumId, page }) { + const optionalAlbum = albumId ? `&albumId=${albumId}` : ''; + + page = page || 1; + + try { + const response = await this.$axios.$get(`search/?q=${encodeURI(q)}${optionalAlbum}`); + + commit('setFilesAndMeta', { ...response, page }); + + return response; + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + + return null; + } +}; + +export const mutations = { + setIsLoading(state) { + state.isLoading = true; + }, + setFilesAndMeta(state, { + files, name, page, count, downloadEnabled + }) { + state.files = files || []; + state.albumName = name ?? null; + state.downloadEnabled = downloadEnabled ?? false; + state.isLoading = false; + state.pagination.page = page || 1; + state.pagination.totalFiles = count || 0; + }, + removeFile(state, fileId) { + const foundIndex = state.files.findIndex(({ id }) => id === fileId); + if (foundIndex > -1) { + state.files.splice(foundIndex, 1); + state.pagination.totalFiles -= 1; + } + }, + setFileAlbums(state, { fileId, albums }) { + Vue.set(state.fileAlbumsMap, fileId, albums); + }, + setFileTags(state, { fileId, tags }) { + Vue.set(state.fileTagsMap, fileId, tags); + }, + setFileExtraInfo(state, { fileId, file }) { + Vue.set(state.fileExtraInfoMap, fileId, file); + }, + addAlbumToFile(state, { fileId, album }) { + if (!state.fileAlbumsMap[fileId]) return; + + state.fileAlbumsMap[fileId].push(album); + }, + removeAlbumFromFile(state, { fileId, albumId }) { + if (!state.fileAlbumsMap[fileId]) return; + + const foundIndex = state.fileAlbumsMap[fileId].findIndex(({ id }) => id === albumId); + if (foundIndex > -1) { + state.fileAlbumsMap[fileId].splice(foundIndex, 1); + } + }, + addTagToFile(state, { fileId, tag }) { + if (!state.fileTagsMap[fileId]) return; + + state.fileTagsMap[fileId].push(tag); + }, + removeTagFromFile(state, { fileId, tag }) { + if (!state.fileTagsMap[fileId]) return; + + const foundIndex = state.fileTagsMap[fileId].findIndex(({ id }) => id === tag.id); + if (foundIndex > -1) { + state.fileTagsMap[fileId].splice(foundIndex, 1); + } + }, + setShowList(state, showList) { + state.showList = showList; + }, + resetState(state) { + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/index.js b/src/site/store/index.js index 1fc2272..c5d9a23 100644 --- a/src/site/store/index.js +++ b/src/site/store/index.js @@ -1,66 +1,21 @@ import config from '../../../dist/config.json'; -export const state = () => ({ - loggedIn: false, - user: null, - token: null, - config: null, - alert: null -}); - -/* eslint-disable no-shadow */ -export const mutations = { - loggedIn(state, payload) { - state.loggedIn = payload; - }, - user(state, payload) { - state.user = payload; - }, - token(state, payload) { - state.token = payload; - }, - config(state, payload) { - state.config = payload; - }, - alert(state, payload) { - state.alert = payload; - } -}; +// eslint-disable-next-line import/prefer-default-export export const actions = { - async nuxtClientInit({ commit, dispatch }, { app, req }) { - commit('config', config); + async nuxtClientInit({ commit, dispatch }) { + commit('config/set', config); const cookies = this.$cookies.getAll(); - if (!cookies.token) return dispatch('logout'); + if (!cookies.token) return dispatch('auth/logout'); - commit('token', cookies.token); - try { - const response = await this.$axios.$get('verify'); - dispatch('login', { - token: cookies.token, - user: response.user - }); - } catch (error) { - // dispatch('logout'); - } - }, - login({ commit }, { token, user }) { - this.$cookies.set('token', token); - commit('token', token); - commit('user', user); - commit('loggedIn', true); - }, - logout({ commit }) { - this.$cookies.remove('token'); - commit('token', null); - commit('user', null); - commit('loggedIn', false); - }, - alert({ commit }, payload) { + commit('auth/setToken', cookies.token); + return dispatch('auth/verify'); + } + /* alert({ commit }, payload) { if (!payload) return commit('alert', null); commit('alert', { text: payload.text, error: payload.error }); - } + } */ }; diff --git a/src/site/store/tags.js b/src/site/store/tags.js new file mode 100644 index 0000000..a28c2ad --- /dev/null +++ b/src/site/store/tags.js @@ -0,0 +1,40 @@ +export const state = () => ({ + tagsList: [] +}); + +export const actions = { + async fetch({ commit }) { + const response = await this.$axios.$get('tags'); + + commit('setTags', response.tags); + + return response; + }, + async createTag({ commit }, name) { + const response = await this.$axios.$post('tag/new', { name }); + + commit('addTag', response.data); + + return response; + }, + async deleteTag({ commit }, tagId) { + const response = await this.$axios.$delete(`tag/${tagId}`); + + commit('deleteTag', response.data); + + return response; + } +}; + +export const mutations = { + setTags(state, tags) { + state.tagsList = tags; + }, + addTag(state, tag) { + state.tagsList.unshift(tag); + }, + deleteTag(state, { id: tagId }) { + const foundIndex = state.tagsList.findIndex(({ id }) => id === tagId); + state.tagsList.splice(foundIndex, 1); + } +}; |