appwrite-server-wrapper
Version:
Wrapper library to handle Appwrite methods including server handling using SSR with NextJS v15 (useActionState, useAction,...)
197 lines (196 loc) • 7.65 kB
JavaScript
import { randomInt } from "crypto";
/**
* Converts an ArrayBuffer to a Base64 string.
* @param buffer - The ArrayBuffer to convert.
* @returns {string} - The Base64 encoded string.
*/
export const arrayBufferToBase64 = (buffer) => {
let binary = "";
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary); // Converts binary string to Base64
};
/**
* Validates if a given string is a valid JSON string.
* @param str - The string to validate.
* @returns {boolean} - True if the string is a valid JSON string, false otherwise.
*/
export const isValidJsonString = (str) => {
try {
JSON.parse(str); // Attempt to parse the string
return true;
}
catch (e) {
return false;
}
};
/**
* Checks if the given object is a valid JSON object.
* @param obj - The object to check.
* @returns {boolean} - True if the object is a valid JSON object, false otherwise.
*/
export const isValidJsonObject = (obj) => {
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
};
/**
* Checks if the given object is an empty object.
* @param obj - The object to check.
* @returns {boolean} - True if the object is empty, false otherwise.
*/
export const isEmptyObject = (obj) => {
return Object.keys(obj).length === 0 && obj.constructor === Object;
};
/**
* Checks if the given object has only one key-value pair with an empty key and an empty value.
* @param obj - The object to check.
* @returns {boolean} - True if the object matches the condition, false otherwise.
*/
export const isEmptyKeyValuePair = (obj) => {
return Object.keys(obj).length === 1 && obj[""] === "";
};
/**
* Generates a random password of the specified length.
*/
export const temporaryPassword = (length = 12) => {
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
let password = "";
for (let i = 0; i < length; i++) {
const randomValue = randomInt(0, charactersLength);
password += characters.charAt(randomValue);
}
return password;
};
/**
* Singleton class for managing appwrite state.
* @class
*/
class AppwriteManager {
static instance;
locale = "de"; // Default locale
isAdmin = false; // Default admin state
constructor() { } // Prevent direct instantiation
static getInstance() {
if (!AppwriteManager.instance) {
AppwriteManager.instance = new AppwriteManager();
}
return AppwriteManager.instance;
}
// Locale management
getLocale() {
return this.locale;
}
setLocale(newLocale) {
this.locale = newLocale;
}
// Admin state management
getAdmin() {
return this.isAdmin;
}
setAdmin(isAdmin) {
this.isAdmin = isAdmin;
}
}
// Export a global instance
export const apwManager = AppwriteManager.getInstance();
/**
* WebP image conversion
* @param file - The image file to convert.
* @param quality - The quality of the output image (0-1).
* @param width - The width of the output image.
* @param height - The height of the output image.
* @param background - The background color of the output image.
* @returns {Promise<File>} - A promise that resolves to the converted image file.
*/
export const imgToWebP = async (file, quality = 0.9, width, height, background = "transparent" // Default is transparent
) => {
return new Promise((resolve, reject) => {
if (!file.type.startsWith("image/")) {
return reject(new Error("Invalid file type"));
}
// Validate background color (allow only hex or transparent)
const isValidHex = /^#([0-9A-F]{3}){1,2}$/i.test(background);
if (background !== "transparent" && !isValidHex) {
return reject(new Error("Invalid background color. Use a hex string."));
}
const reader = new FileReader();
reader.onload = async function (event) {
if (!event.target?.result)
return reject(new Error("File reading failed"));
const img = new Image();
img.src = event.target.result;
img.crossOrigin = "anonymous"; // Prevent CORS issues
img.onload = () => {
let newWidth = img.width;
let newHeight = img.height;
if (width && !height) {
// Scale based on width while maintaining aspect ratio
newHeight = Math.round((img.height / img.width) * width);
newWidth = width;
}
else if (height && !width) {
// Scale based on height while maintaining aspect ratio
newWidth = Math.round((img.width / img.height) * height);
newHeight = height;
}
else if (width && height) {
// Full resize with background padding if aspect ratio doesn't match
const aspectRatio = img.width / img.height;
const targetAspectRatio = width / height;
if (aspectRatio > targetAspectRatio) {
// Image is wider than target aspect ratio
newWidth = width;
newHeight = Math.round(width / aspectRatio);
}
else {
// Image is taller than target aspect ratio
newHeight = height;
newWidth = Math.round(height * aspectRatio);
}
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx)
return reject(new Error("Canvas not supported"));
// Set final canvas size
canvas.width = width || newWidth;
canvas.height = height || newHeight;
// Fill background if a hex color is provided (not transparent)
if (background !== "transparent") {
ctx.fillStyle = background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Calculate center positioning for images with padding
const offsetX = (canvas.width - newWidth) / 2;
const offsetY = (canvas.height - newHeight) / 2;
ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
canvas.toBlob((blob) => {
if (!blob)
return reject(new Error("Failed to create WebP blob"));
resolve(new File([blob], file.name.replace(/\.\w+$/, ".webp"), {
type: "image/webp",
}));
}, "image/webp", quality // Quality (0-1)
);
};
};
reader.onerror = () => reject(new Error("FileReader error"));
reader.readAsDataURL(file);
});
};
export const humanTimeStamp = (timestamp) => {
const date = new Date(timestamp ?? Date.now());
const humanTs = `${date.getFullYear()}${(date.getMonth() + 1)
.toString()
.padStart(2, "0")}${date.getDate().toString().padStart(2, "0")}_${date
.getHours()
.toString()
.padStart(2, "0")}${date.getMinutes().toString().padStart(2, "0")}${date
.getSeconds()
.toString()
.padStart(2, "0")}`;
return humanTs;
};