fix: Enhance dataset controllers with user authentication checks and improve mail configuration
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 59s

This commit is contained in:
Kaimbacher 2025-11-13 11:02:00 +01:00
commit d44d08abcd
7 changed files with 204 additions and 48 deletions

View file

@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- run: echo "The ${{ github.repository }} repository has been cloned to the runner." - run: echo "The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "The workflow is now ready to test your code on the runner." - run: echo "The workflow is now ready to test your code on the runner."
- name: List files in the repository: - name: List files in the repository
run: | run: |
ls ${{ github.workspace }} ls ${{ github.workspace }}
- run: echo "This job's status is ${{ job.status }}." - run: echo "This job's status is ${{ job.status }}."

View file

@ -76,23 +76,24 @@ export default class MailSettingsController {
public async sendTestMail({ response, auth }: HttpContext) { public async sendTestMail({ response, auth }: HttpContext) {
const user = auth.user!; const user = auth.user!;
const userEmail = user.email; const userEmail = user.email;
// let mailManager = await app.container.make('mail.manager'); // let mailManager = await app.container.make('mail.manager');
// let iwas = mailManager.use(); // let iwas = mailManager.use();
// let test = mail.config.mailers.smtp(); // let test = mail.config.mailers.smtp();
if (!userEmail) { if (!userEmail) {
return response.badRequest({ message: 'User email is not set. Please update your profile.' }); return response.badRequest({ message: 'User email is not set. Please update your profile.' });
} }
try { try {
await mail.send((message) => { await mail.send(
message (message) => {
// .from(Config.get('mail.from.address')) message
.from('tethys@geosphere.at') // .from(Config.get('mail.from.address'))
.to(userEmail) .from('tethys@geosphere.at')
.subject('Test Email') .to(userEmail)
.html('<p>If you received this email, the email configuration seems to be correct.</p>'); .subject('Test Email')
}); .html('<p>If you received this email, the email configuration seems to be correct.</p>');
});
return response.json({ success: true, message: 'Test email sent successfully' }); return response.json({ success: true, message: 'Test email sent successfully' });
// return response.flash('Test email sent successfully!', 'message').redirect().back(); // return response.flash('Test email sent successfully!', 'message').redirect().back();

View file

@ -188,10 +188,16 @@ export default class DatasetsController {
} }
} }
public async approve({ request, inertia, response }: HttpContext) { public async approve({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// $dataset = Dataset::with('user:id,login')->findOrFail($id); // $dataset = Dataset::with('user:id,login')->findOrFail($id);
const dataset = await Dataset.findOrFail(id); const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer']; const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
@ -217,7 +223,7 @@ export default class DatasetsController {
}); });
} }
public async approveUpdate({ request, response }: HttpContext) { public async approveUpdate({ request, response, auth }: HttpContext) {
const approveDatasetSchema = vine.object({ const approveDatasetSchema = vine.object({
reviewer_id: vine.number(), reviewer_id: vine.number(),
}); });
@ -230,7 +236,11 @@ export default class DatasetsController {
throw error; throw error;
} }
const id = request.param('id'); const id = request.param('id');
const dataset = await Dataset.findOrFail(id); const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer']; const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
@ -261,10 +271,15 @@ export default class DatasetsController {
} }
} }
public async reject({ request, inertia, response }: HttpContext) { public async reject({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query() const dataset = await Dataset.query()
.where('id', id) .where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
// .preload('titles') // .preload('titles')
// .preload('descriptions') // .preload('descriptions')
.preload('user', (builder) => { .preload('user', (builder) => {
@ -291,10 +306,15 @@ export default class DatasetsController {
public async rejectUpdate({ request, response, auth }: HttpContext) { public async rejectUpdate({ request, response, auth }: HttpContext) {
const authUser = auth.user!; const authUser = auth.user!;
if (!authUser) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const id = request.param('id'); const id = request.param('id');
const dataset = await Dataset.query() const dataset = await Dataset.query()
.where('id', id) .where('id', id)
.where('editor_id', authUser.id) // Ensure the user is the editor of the dataset
.preload('user', (builder) => { .preload('user', (builder) => {
builder.select('id', 'login', 'email'); builder.select('id', 'login', 'email');
}) })
@ -377,9 +397,14 @@ export default class DatasetsController {
public async publish({ request, inertia, response, auth }: HttpContext) { public async publish({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query() const dataset = await Dataset.query()
.where('id', id) .where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.preload('titles') .preload('titles')
.preload('authors') .preload('authors')
// .preload('persons', (builder) => { // .preload('persons', (builder) => {
@ -408,7 +433,7 @@ export default class DatasetsController {
}); });
} }
public async publishUpdate({ request, response }: HttpContext) { public async publishUpdate({ request, response, auth }: HttpContext) {
const publishDatasetSchema = vine.object({ const publishDatasetSchema = vine.object({
publisher_name: vine.string().trim(), publisher_name: vine.string().trim(),
}); });
@ -420,7 +445,12 @@ export default class DatasetsController {
throw error; throw error;
} }
const id = request.param('id'); const id = request.param('id');
const dataset = await Dataset.findOrFail(id); const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).firstOrFail();
// let test = await Dataset.getMax('publish_id'); // let test = await Dataset.getMax('publish_id');
// const maxPublishId = await Database.from('documents').max('publish_id as max_publish_id').first(); // const maxPublishId = await Database.from('documents').max('publish_id as max_publish_id').first();
@ -446,10 +476,16 @@ export default class DatasetsController {
} }
} }
public async rejectToReviewer({ request, inertia, response }: HttpContext) { public async rejectToReviewer({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query() const dataset = await Dataset.query()
.where('id', id) .where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.preload('reviewer', (builder) => { .preload('reviewer', (builder) => {
builder.select('id', 'login', 'email'); builder.select('id', 'login', 'email');
}) })
@ -475,9 +511,14 @@ export default class DatasetsController {
public async rejectToReviewerUpdate({ request, response, auth }: HttpContext) { public async rejectToReviewerUpdate({ request, response, auth }: HttpContext) {
const authUser = auth.user!; const authUser = auth.user!;
if (!authUser) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const id = request.param('id'); const id = request.param('id');
const dataset = await Dataset.query() const dataset = await Dataset.query()
.where('id', id) .where('id', id)
.where('editor_id', authUser.id) // Ensure the user is the editor of the dataset
.preload('reviewer', (builder) => { .preload('reviewer', (builder) => {
builder.select('id', 'login', 'email'); builder.select('id', 'login', 'email');
}) })
@ -558,10 +599,16 @@ export default class DatasetsController {
.toRoute('editor.dataset.list'); .toRoute('editor.dataset.list');
} }
public async doiCreate({ request, inertia }: HttpContext) { public async doiCreate({ request, inertia, auth, response }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query() const dataset = await Dataset.query()
.where('id', id) .where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.preload('titles') .preload('titles')
.preload('descriptions') .preload('descriptions')
// .preload('identifier') // .preload('identifier')
@ -572,11 +619,18 @@ export default class DatasetsController {
}); });
} }
public async doiStore({ request, response }: HttpContext) { public async doiStore({ request, response, auth }: HttpContext) {
const dataId = request.param('publish_id'); const dataId = request.param('publish_id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Load dataset with minimal required relationships // Load dataset with minimal required relationships
const dataset = await Dataset.query().where('publish_id', dataId).firstOrFail(); const dataset = await Dataset.query()
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.where('publish_id', dataId)
.firstOrFail();
const prefix = process.env.DATACITE_PREFIX || ''; const prefix = process.env.DATACITE_PREFIX || '';
const base_domain = process.env.BASE_DOMAIN || ''; const base_domain = process.env.BASE_DOMAIN || '';
@ -658,9 +712,17 @@ export default class DatasetsController {
public async show({}: HttpContext) {} public async show({}: HttpContext) {}
public async edit({ request, inertia, response }: HttpContext) { public async edit({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const datasetQuery = Dataset.query().where('id', id);
// Check if user is authenticated
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Prefilter by both id AND editor_id to ensure user has permission to edit
const datasetQuery = Dataset.query().where('id', id).where('editor_id', user.id);
datasetQuery datasetQuery
.preload('titles', (query) => query.orderBy('id', 'asc')) .preload('titles', (query) => query.orderBy('id', 'asc'))
.preload('descriptions', (query) => query.orderBy('id', 'asc')) .preload('descriptions', (query) => query.orderBy('id', 'asc'))
@ -677,6 +739,7 @@ export default class DatasetsController {
query.orderBy('sort_order', 'asc'); // Sort by sort_order column query.orderBy('sort_order', 'asc'); // Sort by sort_order column
}); });
// This will throw 404 if editor_id does not match logged in user
const dataset = await datasetQuery.firstOrFail(); const dataset = await datasetQuery.firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer']; const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
@ -750,11 +813,16 @@ export default class DatasetsController {
}); });
} }
public async update({ request, response, session }: HttpContext) { public async update({ request, response, session, auth }: HttpContext) {
// Get the dataset id from the route parameter // Get the dataset id from the route parameter
const datasetId = request.param('id'); const datasetId = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Retrieve the dataset and load its existing files // Retrieve the dataset and load its existing files
const dataset = await Dataset.findOrFail(datasetId); const dataset = await Dataset.query().where('id', datasetId).where('editor_id', user.id).firstOrFail();
await dataset.load('files'); await dataset.load('files');
let trx: TransactionClientContract | null = null; let trx: TransactionClientContract | null = null;
@ -763,7 +831,7 @@ export default class DatasetsController {
trx = await db.transaction(); trx = await db.transaction();
// const user = (await User.find(auth.user?.id)) as User; // const user = (await User.find(auth.user?.id)) as User;
// await this.createDatasetAndAssociations(user, request, trx); // await this.createDatasetAndAssociations(user, request, trx);
const dataset = await Dataset.findOrFail(datasetId); // const dataset = await Dataset.findOrFail(datasetId);
// save the licenses // save the licenses
const licenses: number[] = request.input('licenses', []); const licenses: number[] = request.input('licenses', []);
@ -949,10 +1017,15 @@ export default class DatasetsController {
} }
} }
public async categorize({ inertia, request, response }: HttpContext) { public async categorize({ inertia, request, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
// Check if user is authenticated
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Preload dataset and its "collections" relation // Preload dataset and its "collections" relation
const dataset = await Dataset.query().where('id', id).preload('collections').firstOrFail(); const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).preload('collections').firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer']; const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
// session.flash('errors', 'Invalid server state!'); // session.flash('errors', 'Invalid server state!');
@ -980,10 +1053,15 @@ export default class DatasetsController {
}); });
} }
public async categorizeUpdate({ request, response, session }: HttpContext) { public async categorizeUpdate({ request, response, session, auth }: HttpContext) {
// Get the dataset id from the route parameter // Get the dataset id from the route parameter
const id = request.param('id'); const id = request.param('id');
const dataset = await Dataset.query().preload('files').where('id', id).firstOrFail(); const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Retrieve the dataset and load its existing files
const dataset = await Dataset.query().preload('files').where('id', id).where('editor_id', user.id).firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer']; const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
@ -1188,7 +1266,7 @@ export default class DatasetsController {
} }
// return cache.getDomDocument(); // return cache.getDomDocument();
const xmlDocument : XMLBuilder | null = await serializer.toXmlDocument(); const xmlDocument: XMLBuilder | null = await serializer.toXmlDocument();
return xmlDocument; return xmlDocument;
} }
} }

View file

@ -824,13 +824,20 @@ export default class DatasetController {
}; };
// public async release({ params, view }) { // public async release({ params, view }) {
public async release({ request, inertia, response }: HttpContext) { public async release({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query() const dataset = await Dataset.query()
.preload('user', (builder) => { .preload('user', (builder) => {
builder.select('id', 'login'); builder.select('id', 'login');
}) })
.where('account_id', user.id) // Only fetch if user owns it
.where('id', id) .where('id', id)
.firstOrFail(); .firstOrFail();
@ -851,9 +858,20 @@ export default class DatasetController {
}); });
} }
public async releaseUpdate({ request, response }: HttpContext) { public async releaseUpdate({ request, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const dataset = await Dataset.query().preload('files').where('id', id).firstOrFail(); const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.preload('files')
.where('id', id)
.where('account_id', user.id) // Only fetch if user owns it
.firstOrFail();
const validStates = ['inprogress', 'rejected_editor']; const validStates = ['inprogress', 'rejected_editor'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
@ -933,7 +951,15 @@ export default class DatasetController {
public async edit({ request, inertia, response, auth }: HttpContext) { public async edit({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const datasetQuery = Dataset.query().where('id', id); const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Prefilter by both id AND account_id
const datasetQuery = Dataset.query().where('id', id).where('account_id', user.id); // Only fetch if user owns it
datasetQuery datasetQuery
.preload('titles', (query) => query.orderBy('id', 'asc')) .preload('titles', (query) => query.orderBy('id', 'asc'))
.preload('descriptions', (query) => query.orderBy('id', 'asc')) .preload('descriptions', (query) => query.orderBy('id', 'asc'))
@ -949,8 +975,9 @@ export default class DatasetController {
.preload('files', (query) => { .preload('files', (query) => {
query.orderBy('sort_order', 'asc'); // Sort by sort_order column query.orderBy('sort_order', 'asc'); // Sort by sort_order column
}); });
// This will throw 404 if dataset doesn't exist OR user doesn't own it
const dataset = await datasetQuery.firstOrFail(); const dataset = await datasetQuery.firstOrFail();
const validStates = ['inprogress', 'rejected_editor']; const validStates = ['inprogress', 'rejected_editor'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
// session.flash('errors', 'Invalid server state!'); // session.flash('errors', 'Invalid server state!');
@ -1014,11 +1041,30 @@ export default class DatasetController {
}); });
} }
public async update({ request, response, session }: HttpContext) { public async update({ request, response, session, auth }: HttpContext) {
// Get the dataset id from the route parameter // Get the dataset id from the route parameter
const datasetId = request.param('id'); const datasetId = request.param('id');
// Retrieve the dataset and load its existing files const user = auth.user;
const dataset = await Dataset.findOrFail(datasetId);
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to update a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Prefilter by both id AND account_id
const dataset = await Dataset.query()
.where('id', datasetId)
.where('account_id', user.id) // Only fetch if user owns it
.firstOrFail();
// // Check if the authenticated user is the owner of the dataset
// if (dataset.account_id !== user.id) {
// return response
// .flash(`Unauthorized access. You are not the owner of dataset with id ${id}.`, 'error')
// .redirect()
// .toRoute('dataset.list');
// }
await dataset.load('files'); await dataset.load('files');
// Accumulate the size of the already related files // Accumulate the size of the already related files
// const preExistingFileSize = dataset.files.reduce((acc, file) => acc + file.fileSize, 0); // const preExistingFileSize = dataset.files.reduce((acc, file) => acc + file.fileSize, 0);
@ -1442,16 +1488,26 @@ export default class DatasetController {
} }
} }
public async delete({ request, inertia, response, session }: HttpContext) { public async delete({ request, inertia, response, session, auth }: HttpContext) {
const id = request.param('id'); const id = request.param('id');
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
try { try {
// This will throw 404 if dataset doesn't exist OR user doesn't own it
const dataset = await Dataset.query() const dataset = await Dataset.query()
.preload('user', (builder) => { .preload('user', (builder) => {
builder.select('id', 'login'); builder.select('id', 'login');
}) })
.where('id', id) .where('id', id)
.where('account_id', user.id) // Only fetch if user owns it
.preload('files') .preload('files')
.firstOrFail(); .firstOrFail();
const validStates = ['inprogress', 'rejected_editor']; const validStates = ['inprogress', 'rejected_editor'];
if (!validStates.includes(dataset.server_state)) { if (!validStates.includes(dataset.server_state)) {
// session.flash('errors', 'Invalid server state!'); // session.flash('errors', 'Invalid server state!');
@ -1476,9 +1532,27 @@ export default class DatasetController {
} }
} }
public async deleteUpdate({ params, session, response }: HttpContext) { public async deleteUpdate({ params, session, response, auth }: HttpContext) {
try { try {
const dataset = await Dataset.query().where('id', params.id).preload('files').firstOrFail(); const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// This will throw 404 if dataset doesn't exist OR user doesn't own it
const dataset = await Dataset.query()
.where('id', params.id)
.where('account_id', user.id) // Only fetch if user owns it
.preload('files')
.firstOrFail();
// // Check if the authenticated user is the owner of the dataset
// if (dataset.account_id !== user.id) {
// return response
// .flash(`Unauthorized access. You are not the owner of dataset with id ${params.id}.`, 'error')
// .redirect()
// .toRoute('dataset.list');
// }
const validStates = ['inprogress', 'rejected_editor']; const validStates = ['inprogress', 'rejected_editor'];
if (validStates.includes(dataset.server_state)) { if (validStates.includes(dataset.server_state)) {

View file

@ -16,7 +16,7 @@ const mailConfig = defineConfig({
host: env.get('SMTP_HOST', ''), host: env.get('SMTP_HOST', ''),
port: env.get('SMTP_PORT'), port: env.get('SMTP_PORT'),
secure: false, secure: false,
// ignoreTLS: true, ignoreTLS: true,
requireTLS: false, requireTLS: false,
/** /**

View file

@ -87,8 +87,10 @@ import BaseIcon from '@/Components/BaseIcon.vue';
import { MapOptions } from './MapOptions'; import { MapOptions } from './MapOptions';
import { LayerOptions, LayerMap } from './LayerOptions'; import { LayerOptions, LayerMap } from './LayerOptions';
import { MapService } from '@/Stores/map.service'; import { MapService } from '@/Stores/map.service';
import { ZoomControlComponent } from './zoom.component.vue'; // import ZoomControlComponent from '@/Components/Map/zoom.component.vue';
import { DrawControlComponent } from './draw.component.vue'; // import DrawControlComponent from '@/Components/Map/draw.component.vue';
import ZoomControlComponent from './zoom.component.vue';
import DrawControlComponent from './draw.component.vue';
import { Coverage } from '@/Dataset'; import { Coverage } from '@/Dataset';
import { canvas } from 'leaflet/src/layer/vector/Canvas'; import { canvas } from 'leaflet/src/layer/vector/Canvas';
import { svg } from 'leaflet/src/layer/vector/SVG'; import { svg } from 'leaflet/src/layer/vector/SVG';
@ -137,7 +139,7 @@ const DEFAULT_BASE_LAYER_ATTRIBUTION = '&copy; <a target="_blank" href="http://o
BaseIcon, BaseIcon,
}, },
}) })
export class MapComponent extends Vue { export default class MapComponent extends Vue {
@Prop() @Prop()
public mapId: string; public mapId: string;
@ -301,7 +303,7 @@ export class MapComponent extends Vue {
} }
} }
} }
export default MapComponent; // export default MapComponent;
</script> </script>
<style scoped> <style scoped>

View file

@ -933,6 +933,7 @@ import FileUploadComponent from '@/Components/FileUpload.vue';
import { MapOptions } from '@/Components/Map/MapOptions'; import { MapOptions } from '@/Components/Map/MapOptions';
import { LatLngBoundsExpression } from 'leaflet'; import { LatLngBoundsExpression } from 'leaflet';
import { LayerOptions } from '@/Components/Map/LayerOptions'; import { LayerOptions } from '@/Components/Map/LayerOptions';
import BaseIcon from '@/Components/BaseIcon.vue';
import { import {
mdiImageText, mdiImageText,
mdiArrowLeftBoldOutline, mdiArrowLeftBoldOutline,