- Added @t3-oss/env-nextjs for improved environment variable management. - Updated zod to version 4.1.12 for enhanced validation capabilities. - Upgraded jiti to version 2.6.1 for better module loading. - Refactored environment variable imports from env.mjs to env.ts. - Removed deprecated env.mjs file and replaced it with a new env.ts file using @t3-oss/env-nextjs. - Adjusted various components and API routes to utilize the new environment variable structure. - Updated next.config.js to support the new environment variable management. - Modified Docker configuration to align with new BASE_URL handling.
167 lines
5.2 KiB
TypeScript
167 lines
5.2 KiB
TypeScript
import { env } from "~/env";
|
||
import { GetFlagValueHandler } from "~/server/controllers/flags.controller";
|
||
import { db } from "~/server/db";
|
||
import { NewMail } from "~/server/services/mailer";
|
||
|
||
const BASEURL = "https://api.skebby.it/API/v1.0/REST/";
|
||
|
||
enum MessageType {
|
||
HIGH_QUALITY = "GP",
|
||
MEDIUM_QUALITY = "TI",
|
||
LOW_QUALITY = "SI",
|
||
}
|
||
|
||
interface Auth {
|
||
user_key: string;
|
||
session_key: string;
|
||
}
|
||
|
||
interface SMSData {
|
||
/** The type of SMS */
|
||
message_type: MessageType;
|
||
/** The body of the message. *Message max length could be 160 chars when using low-quality SMSs. New line char needs to be codified as “\n”. */
|
||
message: string;
|
||
/** A list of recipents phone numbers. The recipients’ numbers must be in the international format (+393471234567 or 00393471234567) */
|
||
recipient: string[];
|
||
/** The Sender name. If the message type allows a custom TPOA and the field is left empty, the user’s preferred TPOA is used. Must be empty if the message type does not allow a custom TPOA */
|
||
sender?: string;
|
||
/** The messages will be sent at the given scheduled time
|
||
* formats: ddMMyy, yyyyMMdd, ddMMyyHHmm, yyyyMMddHHmmss, yyyy-MM-dd HH:mm, yyyy-MM-dd HH:mm.0 */
|
||
scheduled_delivery_time?: string;
|
||
/** IANA time zone: Europe/Rome, America/New_York, etc. */
|
||
scheduled_delivery_timezone?: string;
|
||
/** Specifies a custom order ID
|
||
* optional, max 32 characters */
|
||
order_id?: string;
|
||
/** Returns the number of used sms credits. */
|
||
returnCredits?: boolean;
|
||
/** Returns the number of remaining SMS credits for the quality used in the sent SMS and for the default user nation */
|
||
returnRemaining?: boolean;
|
||
/** if true, allows sending to invalid recipients */
|
||
allowInvalidRecipients?: boolean;
|
||
/** default is "gsm" */
|
||
encoding?: "gsm" | "ucs2";
|
||
id_landing?: number;
|
||
/** optional, max 64 characters */
|
||
campaign_name?: string;
|
||
/** max number of fragments for the message, default is 7 */
|
||
max_fragments?: number;
|
||
/** if true, truncates the message to fit the max fragments , default is true */
|
||
truncate?: boolean;
|
||
/** Period after which the SMS message will be deleted from the SMS center. */
|
||
validity_period_min?: number;
|
||
richsms_url?: string;
|
||
}
|
||
|
||
export type MinimalSMSData = Omit<SMSData, "message_type" | "sender">;
|
||
interface SMSResponse {
|
||
result: unknown; // or "OK" | any if you're less strict
|
||
[key: string]: unknown; // allow additional unknown keys
|
||
}
|
||
|
||
/**
|
||
* Authenticates the user given their username and password.
|
||
*/
|
||
async function login(username: string, password: string): Promise<Auth> {
|
||
const url = `${BASEURL}login?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`;
|
||
const response = await fetch(url);
|
||
|
||
if (response.status === 401) {
|
||
throw new Error("Authentication failed: Invalid username or password");
|
||
}
|
||
if (response.status === 400) {
|
||
throw new Error(`Error in request: ${await response.text()}`);
|
||
}
|
||
|
||
if (!response.ok || response.status !== 200) {
|
||
throw new Error(`Login failed: ${response.status} ${response.statusText}`);
|
||
}
|
||
|
||
const text = await response.text();
|
||
const [user_key, session_key] = text.split(";");
|
||
if (!user_key || !session_key) {
|
||
throw new Error("Invalid login response format");
|
||
}
|
||
return { session_key, user_key };
|
||
}
|
||
|
||
/**
|
||
* Sends an SMS message
|
||
*/
|
||
async function sendSMS(auth: Auth, smsData: SMSData) {
|
||
const url = `${BASEURL}sms`;
|
||
|
||
const response = await fetch(url, {
|
||
body: JSON.stringify(smsData),
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
Session_key: auth.session_key,
|
||
user_key: auth.user_key,
|
||
},
|
||
method: "POST",
|
||
});
|
||
|
||
if (response.status === 400) {
|
||
throw new Error(`Invalid input: ${await response.text()}`);
|
||
}
|
||
if (response.status === 401) {
|
||
throw new Error("Authentication failed: Invalid user key or session key");
|
||
}
|
||
if (response.status === 404) {
|
||
throw new Error("Not Found: The User_key was not found");
|
||
}
|
||
|
||
if (response.status !== 201) {
|
||
const errorText = await response.text();
|
||
throw new Error(`SMS send failed: ${response.status} ${errorText}`);
|
||
}
|
||
|
||
const data = (await response.json()) as SMSResponse;
|
||
if (data.result !== "OK") {
|
||
throw new Error("SMS send returned non-OK result");
|
||
}
|
||
}
|
||
|
||
// Example usage
|
||
/*const smsData: SMSData = {
|
||
recipient: ["+393407246729"],
|
||
message: "Hello world!",
|
||
message_type: MessageType.HIGH_QUALITY,
|
||
sender: "InfoAlloggi",
|
||
};*/
|
||
|
||
export const SendMessage = async (data: MinimalSMSData) => {
|
||
const smsFlag = await GetFlagValueHandler({
|
||
db: db,
|
||
id: "SMS_OFF",
|
||
});
|
||
if (smsFlag === "true") {
|
||
console.warn(
|
||
`SMS sending is disabled to:${data.recipient.join(", ")} Mgs:${data.message}`,
|
||
);
|
||
return {
|
||
message: `SMS sending is disabled: to:${data.recipient.join(", ")} Mgs:${data.message}`,
|
||
success: true,
|
||
};
|
||
}
|
||
try {
|
||
const auth = await login(env.SKEBBY_USER, env.SKEBBY_PASS);
|
||
await sendSMS(auth, {
|
||
...data,
|
||
message_type: MessageType.HIGH_QUALITY,
|
||
sender: "InfoAlloggi",
|
||
});
|
||
} catch (error) {
|
||
console.error("Error:", error);
|
||
|
||
await NewMail({
|
||
mailType: "generic",
|
||
props: {
|
||
testo: `Errore durante l'invio dell'SMS: to:${data.recipient.join(", ")} Mgs:${data.message}; ${error instanceof Error ? error.message : String(error)}`,
|
||
title: "Errore invio SMS",
|
||
},
|
||
subject: "Errore invio SMS",
|
||
to: "web@infoalloggi.it",
|
||
});
|
||
}
|
||
};
|