- improved vies and controllers for rejecting datasets with email for reviewer and editor role
Some checks failed
CI Pipeline / japa-tests (push) Failing after 1m8s
Some checks failed
CI Pipeline / japa-tests (push) Failing after 1m8s
- falsh also error via config/inertia.ts - npm updates
This commit is contained in:
parent
b06ccae603
commit
2235f3905a
|
@ -21,7 +21,7 @@ export default class LicenseController {
|
||||||
await license.save();
|
await license.save();
|
||||||
|
|
||||||
// session.flash({ message: 'person has been deactivated!' });
|
// session.flash({ message: 'person has been deactivated!' });
|
||||||
return response.flash('person has been deactivated!', 'message').toRoute('settings.license.index')
|
return response.flash('License has been deactivated!', 'message').toRoute('settings.license.index')
|
||||||
}
|
}
|
||||||
|
|
||||||
public async up({ request, response }: HttpContext) {
|
public async up({ request, response }: HttpContext) {
|
||||||
|
@ -31,7 +31,7 @@ export default class LicenseController {
|
||||||
await license.save();
|
await license.save();
|
||||||
|
|
||||||
// session.flash({ message: 'person has been activated!' });
|
// session.flash({ message: 'person has been activated!' });
|
||||||
return response.flash('person has been activated!', 'message').toRoute('settings.license.index');
|
return response.flash('License has been activated!', 'message').toRoute('settings.license.index');
|
||||||
}
|
}
|
||||||
|
|
||||||
// public async edit({ request, inertia }: HttpContext) {
|
// public async edit({ request, inertia }: HttpContext) {
|
||||||
|
|
|
@ -15,9 +15,12 @@ import { DoiClient } from '#app/Library/Doi/DoiClient';
|
||||||
import DoiClientException from '#app/exceptions/DoiClientException';
|
import DoiClientException from '#app/exceptions/DoiClientException';
|
||||||
import logger from '@adonisjs/core/services/logger';
|
import logger from '@adonisjs/core/services/logger';
|
||||||
import { HttpException } from 'node-exceptions';
|
import { HttpException } from 'node-exceptions';
|
||||||
import { ModelQueryBuilderContract } from "@adonisjs/lucid/types/model";
|
import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model';
|
||||||
import vine, { SimpleMessagesProvider } from '@vinejs/vine';
|
import vine, { SimpleMessagesProvider } from '@vinejs/vine';
|
||||||
|
import mail from '@adonisjs/mail/services/main';
|
||||||
|
// import { resolveMx } from 'dns/promises';
|
||||||
|
// import * as net from 'net';
|
||||||
|
import { validate } from 'deep-email-validator';
|
||||||
// Create a new instance of the client
|
// Create a new instance of the client
|
||||||
const client = new Client({ node: 'http://localhost:9200' }); // replace with your OpenSearch endpoint
|
const client = new Client({ node: 'http://localhost:9200' }); // replace with your OpenSearch endpoint
|
||||||
|
|
||||||
|
@ -146,7 +149,7 @@ export default class DatasetsController {
|
||||||
try {
|
try {
|
||||||
await dataset.related('editor').associate(user); // speichert schon ab
|
await dataset.related('editor').associate(user); // speichert schon ab
|
||||||
// await dataset.save();
|
// await dataset.save();
|
||||||
return response.toRoute('editor.dataset.list').flash('message', `You have accepted dataset ${dataset.id}!`);
|
return response.toRoute('editor.dataset.list').flash(`You have accepted dataset ${dataset.id}!`, 'message');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle any errors
|
// Handle any errors
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -231,7 +234,7 @@ export default class DatasetsController {
|
||||||
// .preload('titles')
|
// .preload('titles')
|
||||||
// .preload('descriptions')
|
// .preload('descriptions')
|
||||||
.preload('user', (builder) => {
|
.preload('user', (builder) => {
|
||||||
builder.select('id', 'login');
|
builder.select('id', 'login', 'email');
|
||||||
})
|
})
|
||||||
.firstOrFail();
|
.firstOrFail();
|
||||||
|
|
||||||
|
@ -252,22 +255,86 @@ export default class DatasetsController {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async rejectUpdate({ request, response }: HttpContext) {
|
// private async checkEmailDomain(email: string): Promise<boolean> {
|
||||||
|
// const domain = email.split('@')[1];
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// // Step 1: Check MX records for the domain
|
||||||
|
// const mxRecords = await resolveMx(domain);
|
||||||
|
// if (mxRecords.length === 0) {
|
||||||
|
// return false; // No MX records, can't send email
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Sort MX records by priority
|
||||||
|
// mxRecords.sort((a, b) => a.priority - b.priority);
|
||||||
|
|
||||||
|
// // Step 2: Attempt SMTP connection to the first available mail server
|
||||||
|
// const smtpServer = mxRecords[0].exchange;
|
||||||
|
|
||||||
|
// return await this.checkMailboxExists(smtpServer, email);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Error during MX lookup or SMTP validation:', error);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//// Helper function to check if the mailbox exists using SMTP
|
||||||
|
// private async checkMailboxExists(smtpServer: string, email: string): Promise<boolean> {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// const socket = net.createConnection(25, smtpServer);
|
||||||
|
|
||||||
|
// socket.on('connect', () => {
|
||||||
|
// socket.write(`HELO ${smtpServer}\r\n`);
|
||||||
|
// socket.write(`MAIL FROM: <test@example.com>\r\n`);
|
||||||
|
// socket.write(`RCPT TO: <${email}>\r\n`);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// socket.on('data', (data) => {
|
||||||
|
// const response = data.toString();
|
||||||
|
// if (response.includes('250')) {
|
||||||
|
// // 250 is an SMTP success code
|
||||||
|
// socket.end();
|
||||||
|
// resolve(true); // Email exists
|
||||||
|
// } else if (response.includes('550')) {
|
||||||
|
// // 550 means the mailbox doesn't exist
|
||||||
|
// socket.end();
|
||||||
|
// resolve(false); // Email doesn't exist
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// socket.on('error', (error) => {
|
||||||
|
// console.error('SMTP connection error:', error);
|
||||||
|
// socket.end();
|
||||||
|
// resolve(false);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// socket.on('end', () => {
|
||||||
|
// // SMTP connection closed
|
||||||
|
// });
|
||||||
|
|
||||||
|
// socket.setTimeout(5000, () => {
|
||||||
|
// // Timeout after 5 seconds
|
||||||
|
// socket.end();
|
||||||
|
// resolve(false); // Assume email doesn't exist if no response
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
public async rejectUpdate({ request, response, auth }: HttpContext) {
|
||||||
|
const authUser = auth.user!;
|
||||||
|
|
||||||
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)
|
||||||
.preload('user', (builder) => {
|
.preload('user', (builder) => {
|
||||||
builder.select('id', 'login');
|
builder.select('id', 'login', 'email');
|
||||||
})
|
})
|
||||||
.firstOrFail();
|
.firstOrFail();
|
||||||
|
|
||||||
// const newSchema = schema.create({
|
|
||||||
// server_state: schema.string({ trim: true }),
|
|
||||||
// reject_reviewer_note: schema.string({ trim: true }, [rules.minLength(10), rules.maxLength(500)]),
|
|
||||||
// });
|
|
||||||
const newSchema = vine.object({
|
const newSchema = vine.object({
|
||||||
server_state: vine.string().trim(),
|
server_state: vine.string().trim(),
|
||||||
reject_editor_note: vine.string().trim().minLength(10).maxLength(500),
|
reject_editor_note: vine.string().trim().minLength(10).maxLength(500),
|
||||||
|
send_mail: vine.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -285,29 +352,56 @@ export default class DatasetsController {
|
||||||
// return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back();
|
// return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back();
|
||||||
return response
|
return response
|
||||||
.flash(
|
.flash(
|
||||||
'warning',
|
|
||||||
`Invalid server state. Dataset with id ${id} cannot be rejected. Datset has server state ${dataset.server_state}.`,
|
`Invalid server state. Dataset with id ${id} cannot be rejected. Datset has server state ${dataset.server_state}.`,
|
||||||
|
'warning'
|
||||||
)
|
)
|
||||||
.redirect()
|
.redirect()
|
||||||
.toRoute('reviewer.dataset.list');
|
.toRoute('editor.dataset.list');
|
||||||
}
|
}
|
||||||
|
|
||||||
// dataset.server_state = 'reviewed';
|
|
||||||
dataset.server_state = 'rejected_editor';
|
dataset.server_state = 'rejected_editor';
|
||||||
const rejectEditorNote = request.input('reject_editor_note', '');
|
const rejectEditorNote = request.input('reject_editor_note', '');
|
||||||
dataset.reject_editor_note = rejectEditorNote;
|
dataset.reject_editor_note = rejectEditorNote;
|
||||||
|
|
||||||
try {
|
// add logic for sending reject message
|
||||||
// await dataset.related('editor').associate(user); // speichert schon ab
|
const sendMail = request.input('send_email', false);
|
||||||
await dataset.save();
|
// const validRecipientEmail = await this.checkEmailDomain('arno.kaimbacher@outlook.at');
|
||||||
return response
|
const validationResult = await validate({
|
||||||
.toRoute('editor.dataset.list')
|
email: dataset.user.email,
|
||||||
.flash('message', `You have rejected dataset ${dataset.id}! to submitter ${dataset.user.login}`);
|
validateSMTP: false,
|
||||||
} catch (error) {
|
});
|
||||||
// Handle any errors
|
const validRecipientEmail: boolean = validationResult.valid;
|
||||||
console.error(error);
|
|
||||||
return response.status(500).json({ error: 'An error occurred while reviewing the data.' });
|
let emailStatusMessage = '';
|
||||||
|
|
||||||
|
if (sendMail == true) {
|
||||||
|
if (dataset.user.email && validRecipientEmail) {
|
||||||
|
try {
|
||||||
|
await mail.send((message) => {
|
||||||
|
message.to(dataset.user.email).subject('Dataset Rejection Notification').html(`
|
||||||
|
<p>Dear ${dataset.user.login},</p>
|
||||||
|
<p>Your dataset with ID ${dataset.id} has been rejected.</p>
|
||||||
|
<p>Reason for rejection: ${rejectEditorNote}</p>
|
||||||
|
<p>Best regards,<br>Your Tethys editor: ${authUser.login}</p>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
emailStatusMessage = ` A rejection email was successfully sent to ${dataset.user.email}.`;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return response.flash('Dataset has not been rejected due to an email error: ' + error.message, 'error').toRoute('editor.dataset.list');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emailStatusMessage = ` However, the email could not be sent because the submitter's email address (${dataset.user.email}) is not valid.`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await dataset.save();
|
||||||
|
return response
|
||||||
|
.flash(
|
||||||
|
`You have successfully rejected dataset ${dataset.id} submitted by ${dataset.user.login}.${emailStatusMessage}`,
|
||||||
|
'message',
|
||||||
|
)
|
||||||
|
.toRoute('editor.dataset.list');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async publish({ request, inertia, response }: HttpContext) {
|
public async publish({ request, inertia, response }: HttpContext) {
|
||||||
|
@ -401,10 +495,9 @@ export default class DatasetsController {
|
||||||
|
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
let base_domain = '';
|
let base_domain = '';
|
||||||
// const datacite_environment = process.env.DATACITE_ENVIRONMENT || 'debug';
|
// const datacite_environment = process.env.DATACITE_ENVIRONMENT || 'debug';
|
||||||
prefix = process.env.DATACITE_PREFIX || '';
|
prefix = process.env.DATACITE_PREFIX || '';
|
||||||
base_domain = process.env.BASE_DOMAIN || '';
|
base_domain = process.env.BASE_DOMAIN || '';
|
||||||
|
|
||||||
|
|
||||||
// register DOI:
|
// register DOI:
|
||||||
const doiValue = prefix + '/tethys.' + dataset.publish_id; //'10.21388/tethys.213'
|
const doiValue = prefix + '/tethys.' + dataset.publish_id; //'10.21388/tethys.213'
|
||||||
|
|
|
@ -4,14 +4,16 @@ import Dataset from '#models/dataset';
|
||||||
import Field from '#app/Library/Field';
|
import Field from '#app/Library/Field';
|
||||||
import BaseModel from '#models/base_model';
|
import BaseModel from '#models/base_model';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { ModelQueryBuilderContract } from "@adonisjs/lucid/types/model";
|
import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model';
|
||||||
import vine from '@vinejs/vine';
|
import vine from '@vinejs/vine';
|
||||||
|
import mail from '@adonisjs/mail/services/main';
|
||||||
|
import logger from '@adonisjs/core/services/logger';
|
||||||
|
import { validate } from 'deep-email-validator';
|
||||||
|
|
||||||
interface Dictionary {
|
interface Dictionary {
|
||||||
[index: string]: string;
|
[index: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class DatasetsController {
|
export default class DatasetsController {
|
||||||
public async index({ auth, request, inertia }: HttpContext) {
|
public async index({ auth, request, inertia }: HttpContext) {
|
||||||
const user = (await User.find(auth.user?.id)) as User;
|
const user = (await User.find(auth.user?.id)) as User;
|
||||||
|
@ -196,8 +198,8 @@ export default class DatasetsController {
|
||||||
.where('id', id)
|
.where('id', id)
|
||||||
// .preload('titles')
|
// .preload('titles')
|
||||||
// .preload('descriptions')
|
// .preload('descriptions')
|
||||||
.preload('user', (builder) => {
|
.preload('editor', (builder) => {
|
||||||
builder.select('id', 'login');
|
builder.select('id', 'login', 'email');
|
||||||
})
|
})
|
||||||
.firstOrFail();
|
.firstOrFail();
|
||||||
|
|
||||||
|
@ -218,12 +220,14 @@ export default class DatasetsController {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async rejectUpdate({ request, response }: HttpContext) {
|
public async rejectUpdate({ request, response, auth }: HttpContext) {
|
||||||
|
const authUser = auth.user!;
|
||||||
|
|
||||||
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)
|
||||||
.preload('editor', (builder) => {
|
.preload('editor', (builder) => {
|
||||||
builder.select('id', 'login');
|
builder.select('id', 'login', 'email');
|
||||||
})
|
})
|
||||||
.firstOrFail();
|
.firstOrFail();
|
||||||
|
|
||||||
|
@ -234,6 +238,7 @@ export default class DatasetsController {
|
||||||
const newSchema = vine.object({
|
const newSchema = vine.object({
|
||||||
server_state: vine.string().trim(),
|
server_state: vine.string().trim(),
|
||||||
reject_reviewer_note: vine.string().trim().minLength(10).maxLength(500),
|
reject_reviewer_note: vine.string().trim().minLength(10).maxLength(500),
|
||||||
|
send_mail: vine.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -250,9 +255,9 @@ export default class DatasetsController {
|
||||||
// throw new Error('Invalid server state!');
|
// throw new Error('Invalid server state!');
|
||||||
// return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back();
|
// return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back();
|
||||||
return response
|
return response
|
||||||
.flash(
|
.flash(
|
||||||
'warning',
|
|
||||||
`Invalid server state. Dataset with id ${id} cannot be rejected. Datset has server state ${dataset.server_state}.`,
|
`Invalid server state. Dataset with id ${id} cannot be rejected. Datset has server state ${dataset.server_state}.`,
|
||||||
|
'warning',
|
||||||
)
|
)
|
||||||
.redirect()
|
.redirect()
|
||||||
.toRoute('reviewer.dataset.list');
|
.toRoute('reviewer.dataset.list');
|
||||||
|
@ -263,16 +268,43 @@ export default class DatasetsController {
|
||||||
const rejectReviewerNote = request.input('reject_reviewer_note', '');
|
const rejectReviewerNote = request.input('reject_reviewer_note', '');
|
||||||
dataset.reject_reviewer_note = rejectReviewerNote;
|
dataset.reject_reviewer_note = rejectReviewerNote;
|
||||||
|
|
||||||
try {
|
// add logic for sending reject message
|
||||||
// await dataset.related('editor').associate(user); // speichert schon ab
|
const sendMail = request.input('send_email', false);
|
||||||
await dataset.save();
|
// const validRecipientEmail = await this.checkEmailDomain('arno.kaimbacher@outlook.at');
|
||||||
return response
|
const validationResult = await validate({
|
||||||
.toRoute('reviewer.dataset.list')
|
email: dataset.editor.email,
|
||||||
.flash('message', `You have rejected dataset ${dataset.id}! to editor ${dataset.editor.login}`);
|
validateSMTP: false,
|
||||||
} catch (error) {
|
});
|
||||||
// Handle any errors
|
const validRecipientEmail: boolean = validationResult.valid;
|
||||||
console.error(error);
|
let emailStatusMessage = '';
|
||||||
return response.status(500).json({ error: 'An error occurred while reviewing the data.' });
|
|
||||||
|
if (sendMail == true) {
|
||||||
|
if (dataset.user.email && validRecipientEmail) {
|
||||||
|
try {
|
||||||
|
await mail.send((message) => {
|
||||||
|
message.to(dataset.editor.email).subject('Dataset Rejection Notification').html(`
|
||||||
|
<p>Dear editor ${dataset.editor.login},</p>
|
||||||
|
<p>Your approved dataset with ID ${dataset.id} has been rejected.</p>
|
||||||
|
<p>Reason for rejection: ${rejectReviewerNote}</p>
|
||||||
|
<p>Best regards,<br>Your Tethys reviewer: ${authUser.login}</p>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
emailStatusMessage = ` A rejection email was successfully sent to ${dataset.editor.email}.`;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return response
|
||||||
|
.flash('Dataset has not been rejected due to an email error: ' + error.message, 'error')
|
||||||
|
.toRoute('reviewer.dataset.list');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emailStatusMessage = ` However, the email could not be sent because the editor's email address (${dataset.editor.email}) is not valid.`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await dataset.save();
|
||||||
|
|
||||||
|
return response
|
||||||
|
.toRoute('reviewer.dataset.list')
|
||||||
|
.flash(`You have rejected dataset ${dataset.id}! to editor ${dataset.editor.login}`, 'message');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -771,7 +771,7 @@ export default class DatasetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await dataset.merge(input).save()) {
|
if (await dataset.merge(input).save()) {
|
||||||
return response.toRoute('dataset.list').flash('message', 'You have released your dataset!');
|
return response.toRoute('dataset.list').flash('You have released your dataset!', 'message');
|
||||||
}
|
}
|
||||||
// throw new GeneralException(trans('exceptions.publish.release.update_error'));
|
// throw new GeneralException(trans('exceptions.publish.release.update_error'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default defineConfig({
|
||||||
return {
|
return {
|
||||||
message: ctx.session?.flashMessages.get('message'),
|
message: ctx.session?.flashMessages.get('message'),
|
||||||
warning: ctx.session?.flashMessages.get('warning'),
|
warning: ctx.session?.flashMessages.get('warning'),
|
||||||
|
error: ctx.session?.flashMessages.get('error'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,48 @@
|
||||||
import env from '#start/env'
|
import env from '#start/env';
|
||||||
import { defineConfig, transports } from '@adonisjs/mail'
|
import { defineConfig, transports } from '@adonisjs/mail';
|
||||||
|
|
||||||
const mailConfig = defineConfig({
|
const mailConfig = defineConfig({
|
||||||
default: 'smtp',
|
default: 'smtp',
|
||||||
|
from: 'tethys@geosphere.at',
|
||||||
|
/**
|
||||||
|
* The mailers object can be used to configure multiple mailers
|
||||||
|
* each using a different transport or same transport with different
|
||||||
|
* options.
|
||||||
|
*/
|
||||||
|
mailers: {
|
||||||
|
|
||||||
|
smtp: transports.smtp({
|
||||||
|
socketTimeout: 5000,// Overall timeout (5 seconds)
|
||||||
|
host: env.get('SMTP_HOST', ''),
|
||||||
|
port: env.get('SMTP_PORT'),
|
||||||
|
secure: false,
|
||||||
|
// ignoreTLS: true,
|
||||||
|
requireTLS: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mailers object can be used to configure multiple mailers
|
* Uncomment the auth block if your SMTP
|
||||||
* each using a different transport or same transport with different
|
* server needs authentication
|
||||||
* options.
|
*/
|
||||||
*/
|
/* auth: {
|
||||||
mailers: {
|
|
||||||
smtp: transports.smtp({
|
|
||||||
host: env.get('SMTP_HOST', ''),
|
|
||||||
port: env.get('SMTP_PORT'),
|
|
||||||
secure: false,
|
|
||||||
// ignoreTLS: true,
|
|
||||||
requireTLS: false,
|
|
||||||
/**
|
|
||||||
* Uncomment the auth block if your SMTP
|
|
||||||
* server needs authentication
|
|
||||||
*/
|
|
||||||
/* auth: {
|
|
||||||
type: 'login',
|
type: 'login',
|
||||||
user: env.get('SMTP_USERNAME'),
|
user: env.get('SMTP_USERNAME'),
|
||||||
pass: env.get('SMTP_PASSWORD'),
|
pass: env.get('SMTP_PASSWORD'),
|
||||||
}, */
|
}, */
|
||||||
}),
|
}),
|
||||||
|
|
||||||
resend: transports.resend({
|
|
||||||
key: env.get('RESEND_API_KEY'),
|
|
||||||
baseUrl: 'https://api.resend.com',
|
|
||||||
}),
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export default mailConfig
|
resend: transports.resend({
|
||||||
|
key: env.get('RESEND_API_KEY'),
|
||||||
|
baseUrl: 'https://api.resend.com',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default mailConfig;
|
||||||
|
|
||||||
declare module '@adonisjs/mail/types' {
|
declare module '@adonisjs/mail/types' {
|
||||||
export interface MailersList extends InferMailers<typeof mailConfig> {}
|
export interface MailersList extends InferMailers<typeof mailConfig> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// const mailConfig = defineConfig({
|
// const mailConfig = defineConfig({
|
||||||
// default: 'smtp',
|
// default: 'smtp',
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ declare module '@adonisjs/mail/types' {
|
||||||
// * each using a different transport or same transport with different
|
// * each using a different transport or same transport with different
|
||||||
// * options.
|
// * options.
|
||||||
// */
|
// */
|
||||||
// mailers: {
|
// mailers: {
|
||||||
// smtp: transports.smtp({
|
// smtp: transports.smtp({
|
||||||
// host: env.get('SMTP_HOST', ''),
|
// host: env.get('SMTP_HOST', ''),
|
||||||
// port: env.get('SMTP_PORT'),
|
// port: env.get('SMTP_PORT'),
|
||||||
|
@ -66,11 +65,11 @@ declare module '@adonisjs/mail/types' {
|
||||||
// pass: env.get('SMTP_PASSWORD'),
|
// pass: env.get('SMTP_PASSWORD'),
|
||||||
// }, */
|
// }, */
|
||||||
// }),
|
// }),
|
||||||
|
|
||||||
// resend: transports.resend({
|
// resend: transports.resend({
|
||||||
// key: env.get('RESEND_API_KEY'),
|
// key: env.get('RESEND_API_KEY'),
|
||||||
// baseUrl: 'https://api.resend.com',
|
// baseUrl: 'https://api.resend.com',
|
||||||
// }),
|
// }),
|
||||||
|
|
||||||
// },
|
// },
|
||||||
// })
|
// })
|
||||||
|
|
635
package-lock.json
generated
635
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -44,7 +44,7 @@
|
||||||
"@types/escape-html": "^1.0.4",
|
"@types/escape-html": "^1.0.4",
|
||||||
"@types/leaflet": "^1.9.3",
|
"@types/leaflet": "^1.9.3",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^20.1.1",
|
"@types/node": "^22.5.5",
|
||||||
"@types/proxy-addr": "^2.0.0",
|
"@types/proxy-addr": "^2.0.0",
|
||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"@types/source-map-support": "^0.5.6",
|
"@types/source-map-support": "^0.5.6",
|
||||||
|
@ -54,14 +54,14 @@
|
||||||
"babel-preset-typescript-vue3": "^2.0.17",
|
"babel-preset-typescript-vue3": "^2.0.17",
|
||||||
"chart.js": "^4.2.0",
|
"chart.js": "^4.2.0",
|
||||||
"dotenv-webpack": "^8.0.1",
|
"dotenv-webpack": "^8.0.1",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-adonis": "^2.1.1",
|
"eslint-plugin-adonis": "^2.1.1",
|
||||||
"eslint-plugin-prettier": "^5.0.0-alpha.2",
|
"eslint-plugin-prettier": "^5.0.0-alpha.2",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
"pinia": "^2.0.30",
|
"pinia": "^2.0.30",
|
||||||
"pino-pretty": "^11.2.2",
|
"pino-pretty": "^11.2.2",
|
||||||
"postcss-loader": "^7.3.4",
|
"postcss-loader": "^8.1.1",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"supertest": "^6.3.3",
|
"supertest": "^6.3.3",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
|
@ -99,6 +99,7 @@
|
||||||
"clamscan": "^2.1.2",
|
"clamscan": "^2.1.2",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
|
"deep-email-validator": "^0.1.21",
|
||||||
"edge.js": "^6.0.1",
|
"edge.js": "^6.0.1",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"focus-trap": "^7.5.4",
|
"focus-trap": "^7.5.4",
|
||||||
|
|
|
@ -94,15 +94,19 @@ const formatServerState = (state: string) => {
|
||||||
<template>
|
<template>
|
||||||
<LayoutAuthenticated>
|
<LayoutAuthenticated>
|
||||||
|
|
||||||
<Head title="Dataset List" />
|
<Head title="Editor Datasets" />
|
||||||
<SectionMain>
|
<SectionMain>
|
||||||
<!-- <FormValidationErrors v-bind:errors="errors" /> -->
|
|
||||||
|
|
||||||
<NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline">
|
<NotificationBar v-if="flash.message" color="success" :icon="mdiAlertBoxOutline">
|
||||||
{{ flash.message }}
|
{{ flash.message }}
|
||||||
</NotificationBar>
|
</NotificationBar>
|
||||||
<NotificationBar v-if="flash.warning" color="warning" :icon="mdiAlertBoxOutline">
|
<NotificationBar v-if="flash.warning" color="warning" :icon="mdiAlertBoxOutline">
|
||||||
{{ flash.warning }}
|
{{ flash.warning }}
|
||||||
</NotificationBar>
|
</NotificationBar>
|
||||||
|
<NotificationBar v-if="flash.error" color="danger" :icon="mdiAlertBoxOutline">
|
||||||
|
{{ flash.error }}
|
||||||
|
</NotificationBar>
|
||||||
|
|
||||||
<!-- table -->
|
<!-- table -->
|
||||||
<CardBox class="mb-6" has-table>
|
<CardBox class="mb-6" has-table>
|
||||||
|
|
|
@ -22,8 +22,12 @@ const props = defineProps({
|
||||||
|
|
||||||
// Define the computed property for the label
|
// Define the computed property for the label
|
||||||
const computedLabel = computed(() => {
|
const computedLabel = computed(() => {
|
||||||
return `Reject to ${props.dataset.user?.login || 'Unknown User'}`;
|
return `Reject to submitter: ${props.dataset.user?.login || 'Unknown User'}`;
|
||||||
});
|
});
|
||||||
|
const computedEmailLabel = computed(() => {
|
||||||
|
return props.dataset.user?.email || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const flash: Ref<any> = computed(() => {
|
const flash: Ref<any> = computed(() => {
|
||||||
return usePage().props.flash;
|
return usePage().props.flash;
|
||||||
|
@ -35,6 +39,7 @@ const errors: Ref<any> = computed(() => {
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
server_state: 'rejected_editor',
|
server_state: 'rejected_editor',
|
||||||
reject_editor_note: '',
|
reject_editor_note: '',
|
||||||
|
send_email: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = async (e: SubmitEvent) => {
|
const handleSubmit = async (e: SubmitEvent) => {
|
||||||
|
@ -75,6 +80,24 @@ const handleSubmit = async (e: SubmitEvent) => {
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
<!-- <FormControl
|
||||||
|
type="checkbox"
|
||||||
|
v-model="form.send_email"
|
||||||
|
:error="form.errors.send_email">
|
||||||
|
</FormControl> -->
|
||||||
|
|
||||||
|
<FormField label="Email Notification">
|
||||||
|
<label for="send_email" class="flex items-center mr-6 mb-3">
|
||||||
|
<input type="checkbox" id="send_email" v-model="form.send_email" class="mr-2" />
|
||||||
|
<span class="check"></span>
|
||||||
|
<a class="pl-2 " target="_blank">send email to
|
||||||
|
<span class="text-blue-600 hover:underline">
|
||||||
|
{{ computedEmailLabel }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
|
||||||
<!-- <NotificationBar v-if="flash && flash.message" color="warning" :icon="mdiAlertBoxOutline">
|
<!-- <NotificationBar v-if="flash && flash.message" color="warning" :icon="mdiAlertBoxOutline">
|
||||||
{{ flash.message }}
|
{{ flash.message }}
|
||||||
|
|
|
@ -19,6 +19,12 @@ const props = defineProps({
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const computedLabel = computed(() => {
|
||||||
|
return `Reject to editor: ${props.dataset.editor?.login || ''}`;
|
||||||
|
});
|
||||||
|
const computedEmailLabel = computed(() => {
|
||||||
|
return props.dataset.editor?.email || '';
|
||||||
|
});
|
||||||
|
|
||||||
const flash: Ref<any> = computed(() => {
|
const flash: Ref<any> = computed(() => {
|
||||||
return usePage().props.flash;
|
return usePage().props.flash;
|
||||||
|
@ -30,9 +36,10 @@ const errors: Ref<any> = computed(() => {
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
server_state: 'rejected_reviewer',
|
server_state: 'rejected_reviewer',
|
||||||
reject_reviewer_note: '',
|
reject_reviewer_note: '',
|
||||||
|
send_email: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e: SubmitEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await form.put(stardust.route('reviewer.dataset.rejectUpdate', [props.dataset.id]));
|
await form.put(stardust.route('reviewer.dataset.rejectUpdate', [props.dataset.id]));
|
||||||
// await form.put(stardust.route('editor.dataset.update', [props.dataset.id]));
|
// await form.put(stardust.route('editor.dataset.update', [props.dataset.id]));
|
||||||
|
@ -63,13 +70,25 @@ const handleSubmit = async (e) => {
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="reject note" :class="{ 'text-red-400': form.errors.reject_reviewer_note }">
|
<FormField label="reject note" :class="{ 'text-red-400': form.errors.reject_reviewer_note }">
|
||||||
<FormControl v-model="form.reject_reviewer_note" type="textarea"
|
<FormControl v-model="form.reject_reviewer_note" type="textarea"
|
||||||
placeholder="-- reject note for editor --" :error="form.errors.reject_reviewer_note">
|
:placeholder="`-- reject note for editor ${dataset.editor.login}--`" :error="form.errors.reject_reviewer_note">
|
||||||
<div class="text-red-400 text-sm" v-if="form.errors.reject_reviewer_note">
|
<div class="text-red-400 text-sm" v-if="form.errors.reject_reviewer_note">
|
||||||
{{ form.errors.reject_reviewer_note }}
|
{{ form.errors.reject_reviewer_note }}
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
<FormField label="Email Notification">
|
||||||
|
<label for="send_email" class="flex items-center mr-6 mb-3">
|
||||||
|
<input type="checkbox" id="send_email" v-model="form.send_email" class="mr-2" />
|
||||||
|
<span class="check"></span>
|
||||||
|
<a class="pl-2 " target="_blank">send email to
|
||||||
|
<span class="text-blue-600 hover:underline">
|
||||||
|
{{ computedEmailLabel }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
|
||||||
<!-- <NotificationBar v-if="flash && flash.message" color="warning" :icon="mdiAlertBoxOutline">
|
<!-- <NotificationBar v-if="flash && flash.message" color="warning" :icon="mdiAlertBoxOutline">
|
||||||
{{ flash.message }}
|
{{ flash.message }}
|
||||||
|
@ -84,7 +103,7 @@ const handleSubmit = async (e) => {
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<BaseButtons>
|
<BaseButtons>
|
||||||
<BaseButton type="submit" color="info" label="Reject to editor"
|
<BaseButton type="submit" color="info" :label="computedLabel"
|
||||||
:class="{ 'opacity-25': form.processing }" :disabled="form.processing" />
|
:class="{ 'opacity-25': form.processing }" :disabled="form.processing" />
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -31,7 +31,7 @@ declare module '@adonisjs/core/http' {
|
||||||
// this.ctx!.session.flash(key, message);
|
// this.ctx!.session.flash(key, message);
|
||||||
// return this;
|
// return this;
|
||||||
// });
|
// });
|
||||||
Response.macro('flash', function (this: Response, message: any, key?: string,) {
|
Response.macro('flash', function (this: Response, message: any, key?: string) {
|
||||||
if (!this.ctx) {
|
if (!this.ctx) {
|
||||||
throw new Error('Context is not available');
|
throw new Error('Context is not available');
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ Response.macro('toRoute', function (this: Response, route: string) {
|
||||||
declare module '@adonisjs/core/http' {
|
declare module '@adonisjs/core/http' {
|
||||||
interface Response {
|
interface Response {
|
||||||
flash(message: any): Response;
|
flash(message: any): Response;
|
||||||
flash(key: string, message: any): Response;
|
flash(message: any, key: string): Response;
|
||||||
toRoute(route: string): Response;
|
toRoute(route: string): Response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user