Remove sharp dependency and related configurations from package.json and pnpm-lock.yaml; update Prisma configuration for database connection handling and streamline database commands in BFF application. Clean up unused migration files and adjust schema.prisma to reflect recent model changes.
This commit is contained in:
parent
424f257bd7
commit
0e32d4004a
@ -24,10 +24,10 @@
|
||||
"type-check": "tsc --project tsconfig.json --noEmit",
|
||||
"type-check:watch": "tsc --project tsconfig.json --noEmit --watch",
|
||||
"clean": "rm -rf dist tsconfig.build.tsbuildinfo",
|
||||
"db:migrate": "prisma migrate dev --schema=prisma/schema.prisma",
|
||||
"db:generate": "prisma generate --schema=prisma/schema.prisma",
|
||||
"db:studio": "prisma studio --schema=prisma/schema.prisma",
|
||||
"db:reset": "prisma migrate reset --schema=prisma/schema.prisma",
|
||||
"db:migrate": "prisma migrate dev --config prisma/prisma.config.ts",
|
||||
"db:generate": "prisma generate --config prisma/prisma.config.ts",
|
||||
"db:studio": "prisma studio --port 5555 --config prisma/prisma.config.ts",
|
||||
"db:reset": "prisma migrate reset --config prisma/prisma.config.ts",
|
||||
"db:seed": "tsx prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "public"."UserRole" AS ENUM ('USER', 'ADMIN');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "public"."JobStatus" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED', 'RETRYING');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "public"."AuditAction" AS ENUM ('LOGIN_SUCCESS', 'LOGIN_FAILED', 'LOGOUT', 'SIGNUP', 'PASSWORD_RESET', 'PASSWORD_CHANGE', 'ACCOUNT_LOCKED', 'ACCOUNT_UNLOCKED', 'PROFILE_UPDATE', 'MFA_ENABLED', 'MFA_DISABLED', 'API_ACCESS');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."users" (
|
||||
"id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"password_hash" TEXT,
|
||||
"first_name" TEXT,
|
||||
"last_name" TEXT,
|
||||
"company" TEXT,
|
||||
"phone" TEXT,
|
||||
"role" "public"."UserRole" NOT NULL DEFAULT 'USER',
|
||||
"mfa_secret" TEXT,
|
||||
"email_verified" BOOLEAN NOT NULL DEFAULT false,
|
||||
"failed_login_attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"locked_until" TIMESTAMP(3),
|
||||
"last_login_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."id_mappings" (
|
||||
"user_id" TEXT NOT NULL,
|
||||
"whmcs_client_id" INTEGER NOT NULL,
|
||||
"sf_account_id" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "id_mappings_pkey" PRIMARY KEY ("user_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."invoices_mirror" (
|
||||
"invoice_id" INTEGER NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"number" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL,
|
||||
"amount_cents" INTEGER NOT NULL,
|
||||
"currency" CHAR(3) NOT NULL,
|
||||
"due_date" DATE,
|
||||
"issued_at" TIMESTAMP(3),
|
||||
"paid_at" TIMESTAMP(3),
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "invoices_mirror_pkey" PRIMARY KEY ("invoice_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."subscriptions_mirror" (
|
||||
"service_id" INTEGER NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"product_name" TEXT NOT NULL,
|
||||
"domain" TEXT,
|
||||
"cycle" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL,
|
||||
"next_due" TIMESTAMP(3),
|
||||
"amount_cents" INTEGER NOT NULL,
|
||||
"currency" CHAR(3) NOT NULL,
|
||||
"registered_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "subscriptions_mirror_pkey" PRIMARY KEY ("service_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."idempotency_keys" (
|
||||
"key" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "idempotency_keys_pkey" PRIMARY KEY ("key")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."jobs" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"data" JSONB NOT NULL,
|
||||
"status" "public"."JobStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"max_retries" INTEGER NOT NULL DEFAULT 3,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"processed_at" TIMESTAMP(3),
|
||||
"failed_at" TIMESTAMP(3),
|
||||
"error" TEXT,
|
||||
|
||||
CONSTRAINT "jobs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."audit_logs" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT,
|
||||
"action" "public"."AuditAction" NOT NULL,
|
||||
"resource" TEXT,
|
||||
"details" JSONB,
|
||||
"ip_address" TEXT,
|
||||
"user_agent" TEXT,
|
||||
"success" BOOLEAN NOT NULL DEFAULT true,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_email_key" ON "public"."users"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "id_mappings_whmcs_client_id_key" ON "public"."id_mappings"("whmcs_client_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "invoices_mirror_user_id_status_idx" ON "public"."invoices_mirror"("user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "invoices_mirror_user_id_due_date_idx" ON "public"."invoices_mirror"("user_id", "due_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "subscriptions_mirror_user_id_status_idx" ON "public"."subscriptions_mirror"("user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "subscriptions_mirror_user_id_next_due_idx" ON "public"."subscriptions_mirror"("user_id", "next_due");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idempotency_keys_user_id_idx" ON "public"."idempotency_keys"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idempotency_keys_created_at_idx" ON "public"."idempotency_keys"("created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "jobs_status_created_at_idx" ON "public"."jobs"("status", "created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_user_id_action_idx" ON "public"."audit_logs"("user_id", "action");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_action_created_at_idx" ON "public"."audit_logs"("action", "created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_created_at_idx" ON "public"."audit_logs"("created_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."id_mappings" ADD CONSTRAINT "id_mappings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."invoices_mirror" ADD CONSTRAINT "invoices_mirror_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."subscriptions_mirror" ADD CONSTRAINT "subscriptions_mirror_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."idempotency_keys" ADD CONSTRAINT "idempotency_keys_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."audit_logs" ADD CONSTRAINT "audit_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@ -1,17 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."sim_usage_daily" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"account" TEXT NOT NULL,
|
||||
"date" DATE NOT NULL,
|
||||
"usageMb" DOUBLE PRECISION NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "sim_usage_daily_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_usage_daily_account_date_idx" ON "public"."sim_usage_daily"("account", "date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sim_usage_daily_account_date_key" ON "public"."sim_usage_daily"("account", "date");
|
||||
@ -1,14 +0,0 @@
|
||||
-- Safety migration to ensure cached profile columns stay removed.
|
||||
-- Some environments created an empty migration folder, so deploys failed
|
||||
-- because Prisma could not find migration.sql. This keeps the history intact
|
||||
-- while acting as a no-op when the columns already dropped earlier.
|
||||
|
||||
ALTER TABLE "public"."users"
|
||||
DROP COLUMN IF EXISTS "first_name",
|
||||
DROP COLUMN IF EXISTS "last_name",
|
||||
DROP COLUMN IF EXISTS "company",
|
||||
DROP COLUMN IF EXISTS "phone";
|
||||
|
||||
COMMENT ON TABLE "public"."users"
|
||||
IS 'Portal authentication only. Profile data fetched from WHMCS via IdMapping.';
|
||||
|
||||
195
apps/bff/prisma/migrations/20251211045819_init/migration.sql
Normal file
195
apps/bff/prisma/migrations/20251211045819_init/migration.sql
Normal file
@ -0,0 +1,195 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "UserRole" AS ENUM ('USER', 'ADMIN');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AuditAction" AS ENUM ('LOGIN_SUCCESS', 'LOGIN_FAILED', 'LOGOUT', 'SIGNUP', 'PASSWORD_RESET', 'PASSWORD_CHANGE', 'ACCOUNT_LOCKED', 'ACCOUNT_UNLOCKED', 'PROFILE_UPDATE', 'MFA_ENABLED', 'MFA_DISABLED', 'API_ACCESS', 'SYSTEM_MAINTENANCE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SmsType" AS ENUM ('DOMESTIC', 'INTERNATIONAL');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"password_hash" TEXT,
|
||||
"role" "UserRole" NOT NULL DEFAULT 'USER',
|
||||
"mfa_secret" TEXT,
|
||||
"email_verified" BOOLEAN NOT NULL DEFAULT false,
|
||||
"failed_login_attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"locked_until" TIMESTAMP(3),
|
||||
"last_login_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "id_mappings" (
|
||||
"user_id" TEXT NOT NULL,
|
||||
"whmcs_client_id" INTEGER NOT NULL,
|
||||
"sf_account_id" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "id_mappings_pkey" PRIMARY KEY ("user_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "audit_logs" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT,
|
||||
"action" "AuditAction" NOT NULL,
|
||||
"resource" TEXT,
|
||||
"details" JSONB,
|
||||
"ip_address" TEXT,
|
||||
"user_agent" TEXT,
|
||||
"success" BOOLEAN NOT NULL DEFAULT true,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sim_usage_daily" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"account" TEXT NOT NULL,
|
||||
"date" DATE NOT NULL,
|
||||
"usageMb" DOUBLE PRECISION NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "sim_usage_daily_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sim_voice_options" (
|
||||
"account" TEXT NOT NULL,
|
||||
"voice_mail_enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"call_waiting_enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"international_roaming_enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"network_type" TEXT NOT NULL DEFAULT '4G',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "sim_voice_options_pkey" PRIMARY KEY ("account")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sim_call_history_domestic" (
|
||||
"id" TEXT NOT NULL,
|
||||
"account" TEXT NOT NULL,
|
||||
"call_date" DATE NOT NULL,
|
||||
"call_time" TEXT NOT NULL,
|
||||
"called_to" TEXT NOT NULL,
|
||||
"location" TEXT,
|
||||
"duration_sec" INTEGER NOT NULL,
|
||||
"charge_yen" INTEGER NOT NULL,
|
||||
"month" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "sim_call_history_domestic_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sim_call_history_international" (
|
||||
"id" TEXT NOT NULL,
|
||||
"account" TEXT NOT NULL,
|
||||
"call_date" DATE NOT NULL,
|
||||
"start_time" TEXT NOT NULL,
|
||||
"stop_time" TEXT,
|
||||
"country" TEXT,
|
||||
"called_to" TEXT NOT NULL,
|
||||
"duration_sec" INTEGER NOT NULL,
|
||||
"charge_yen" INTEGER NOT NULL,
|
||||
"month" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "sim_call_history_international_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sim_sms_history" (
|
||||
"id" TEXT NOT NULL,
|
||||
"account" TEXT NOT NULL,
|
||||
"sms_date" DATE NOT NULL,
|
||||
"sms_time" TEXT NOT NULL,
|
||||
"sent_to" TEXT NOT NULL,
|
||||
"sms_type" "SmsType" NOT NULL DEFAULT 'DOMESTIC',
|
||||
"month" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "sim_sms_history_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sim_history_imports" (
|
||||
"id" TEXT NOT NULL,
|
||||
"month" TEXT NOT NULL,
|
||||
"talk_file" TEXT,
|
||||
"sms_file" TEXT,
|
||||
"talk_records" INTEGER NOT NULL DEFAULT 0,
|
||||
"sms_records" INTEGER NOT NULL DEFAULT 0,
|
||||
"imported_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"status" TEXT NOT NULL DEFAULT 'completed',
|
||||
|
||||
CONSTRAINT "sim_history_imports_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "id_mappings_whmcs_client_id_key" ON "id_mappings"("whmcs_client_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_user_id_action_idx" ON "audit_logs"("user_id", "action");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_action_created_at_idx" ON "audit_logs"("action", "created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_logs_created_at_idx" ON "audit_logs"("created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_usage_daily_account_date_idx" ON "sim_usage_daily"("account", "date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sim_usage_daily_account_date_key" ON "sim_usage_daily"("account", "date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_call_history_domestic_account_month_idx" ON "sim_call_history_domestic"("account", "month");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_call_history_domestic_account_call_date_idx" ON "sim_call_history_domestic"("account", "call_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sim_call_history_domestic_account_call_date_call_time_calle_key" ON "sim_call_history_domestic"("account", "call_date", "call_time", "called_to");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_call_history_international_account_month_idx" ON "sim_call_history_international"("account", "month");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_call_history_international_account_call_date_idx" ON "sim_call_history_international"("account", "call_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sim_call_history_international_account_call_date_start_time_key" ON "sim_call_history_international"("account", "call_date", "start_time", "called_to");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_sms_history_account_month_idx" ON "sim_sms_history"("account", "month");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sim_sms_history_account_sms_date_idx" ON "sim_sms_history"("account", "sms_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sim_sms_history_account_sms_date_sms_time_sent_to_key" ON "sim_sms_history"("account", "sms_date", "sms_time", "sent_to");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sim_history_imports_month_key" ON "sim_history_imports"("month");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "id_mappings" ADD CONSTRAINT "id_mappings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@ -1,9 +0,0 @@
|
||||
-- Force password reset for all users due to migration from bcrypt to argon2
|
||||
-- bcrypt hashes are incompatible with argon2, so all users must reset their passwords
|
||||
|
||||
-- Set all password hashes to NULL, which will require users to go through password reset flow
|
||||
UPDATE "User" SET password_hash = NULL WHERE password_hash IS NOT NULL;
|
||||
|
||||
-- Log the migration for audit purposes (optional - create entry in audit_log if table exists)
|
||||
-- This is a data migration, not a schema change
|
||||
|
||||
@ -1,29 +1,22 @@
|
||||
import { defineConfig } from "prisma";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { Pool } from "pg";
|
||||
import { defineConfig } from "prisma/config";
|
||||
|
||||
// Default connection for local development (matches docker/dev/docker-compose.yml)
|
||||
const DEFAULT_DATABASE_URL = "postgresql://dev:dev@localhost:5432/portal_dev";
|
||||
|
||||
/**
|
||||
* Prisma 7 Configuration
|
||||
*
|
||||
* This configuration file is required for Prisma 7+ where the datasource URL
|
||||
* is no longer specified in schema.prisma. Instead, connection configuration
|
||||
* is provided here for migrations and in the PrismaClient constructor for runtime.
|
||||
* Centralized configuration for Prisma CLI commands (migrate, studio, etc.)
|
||||
* Runtime adapter is configured in PrismaService, not here.
|
||||
*
|
||||
* @see https://pris.ly/d/config-datasource
|
||||
* @see https://pris.ly/d/prisma7-client-config
|
||||
* @see https://www.prisma.io/docs/orm/reference/prisma-config-reference
|
||||
* @see https://www.prisma.io/docs/orm/prisma-schema/overview/data-sources
|
||||
*/
|
||||
export default defineConfig({
|
||||
earlyAccess: true,
|
||||
schema: "./schema.prisma",
|
||||
migrate: {
|
||||
adapter: async () => {
|
||||
const connectionString = process.env.DATABASE_URL;
|
||||
if (!connectionString) {
|
||||
throw new Error("DATABASE_URL environment variable is required for migrations");
|
||||
}
|
||||
const pool = new Pool({ connectionString });
|
||||
return new PrismaPg(pool);
|
||||
},
|
||||
// Database connection for CLI commands (migrate, studio, etc.)
|
||||
datasource: {
|
||||
url: process.env.DATABASE_URL || DEFAULT_DATABASE_URL,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -15,30 +15,27 @@ generator client {
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
// Note: Prisma 7+ requires connection URL to be passed via adapter in PrismaClient constructor
|
||||
// See prisma.config.ts for migration configuration
|
||||
// Prisma 7+: URL is configured via prisma.config.ts for migrations
|
||||
// and via --url flag for studio. Runtime uses the adapter in PrismaClient.
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
passwordHash String? @map("password_hash")
|
||||
role UserRole @default(USER)
|
||||
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
passwordHash String? @map("password_hash")
|
||||
role UserRole @default(USER)
|
||||
|
||||
// Authentication state only - profile data comes from WHMCS
|
||||
mfaSecret String? @map("mfa_secret")
|
||||
emailVerified Boolean @default(false) @map("email_verified")
|
||||
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
|
||||
lockedUntil DateTime? @map("locked_until")
|
||||
lastLoginAt DateTime? @map("last_login_at")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
mfaSecret String? @map("mfa_secret")
|
||||
emailVerified Boolean @default(false) @map("email_verified")
|
||||
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
|
||||
lockedUntil DateTime? @map("locked_until")
|
||||
lastLoginAt DateTime? @map("last_login_at")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
auditLogs AuditLog[]
|
||||
idMapping IdMapping?
|
||||
idempotencyKeys IdempotencyKey[]
|
||||
invoicesMirror InvoiceMirror[]
|
||||
subscriptionsMirror SubscriptionMirror[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
@ -54,71 +51,6 @@ model IdMapping {
|
||||
@@map("id_mappings")
|
||||
}
|
||||
|
||||
model InvoiceMirror {
|
||||
invoiceId Int @id @map("invoice_id")
|
||||
userId String @map("user_id")
|
||||
number String
|
||||
status String
|
||||
amountCents Int @map("amount_cents")
|
||||
currency String @db.Char(3)
|
||||
dueDate DateTime? @map("due_date") @db.Date
|
||||
issuedAt DateTime? @map("issued_at")
|
||||
paidAt DateTime? @map("paid_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, status])
|
||||
@@index([userId, dueDate])
|
||||
@@map("invoices_mirror")
|
||||
}
|
||||
|
||||
model SubscriptionMirror {
|
||||
serviceId Int @id @map("service_id")
|
||||
userId String @map("user_id")
|
||||
productName String @map("product_name")
|
||||
domain String?
|
||||
cycle String
|
||||
status String
|
||||
nextDue DateTime? @map("next_due")
|
||||
amountCents Int @map("amount_cents")
|
||||
currency String @db.Char(3)
|
||||
registeredAt DateTime @map("registered_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, status])
|
||||
@@index([userId, nextDue])
|
||||
@@map("subscriptions_mirror")
|
||||
}
|
||||
|
||||
model IdempotencyKey {
|
||||
key String @id
|
||||
userId String @map("user_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
@@index([createdAt])
|
||||
@@map("idempotency_keys")
|
||||
}
|
||||
|
||||
model Job {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
data Json
|
||||
status JobStatus @default(PENDING)
|
||||
attempts Int @default(0)
|
||||
maxRetries Int @default(3) @map("max_retries")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
processedAt DateTime? @map("processed_at")
|
||||
failedAt DateTime? @map("failed_at")
|
||||
error String?
|
||||
|
||||
@@index([status, createdAt])
|
||||
@@map("jobs")
|
||||
}
|
||||
|
||||
model AuditLog {
|
||||
id String @id @default(uuid())
|
||||
userId String? @map("user_id")
|
||||
@ -143,14 +75,6 @@ enum UserRole {
|
||||
ADMIN
|
||||
}
|
||||
|
||||
enum JobStatus {
|
||||
PENDING
|
||||
PROCESSING
|
||||
COMPLETED
|
||||
FAILED
|
||||
RETRYING
|
||||
}
|
||||
|
||||
enum AuditAction {
|
||||
LOGIN_SUCCESS
|
||||
LOGIN_FAILED
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { INestApplication } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import type { NestExpressApplication } from "@nestjs/platform-express";
|
||||
import { Logger } from "nestjs-pino";
|
||||
import helmet from "helmet";
|
||||
import cookieParser from "cookie-parser";
|
||||
import * as express from "express";
|
||||
import type { CookieOptions, Response, NextFunction, Request } from "express";
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
@ -22,12 +22,15 @@ import { UnifiedExceptionFilter } from "../core/http/exception.filter.js";
|
||||
import { AppModule } from "../app.module.js";
|
||||
|
||||
export async function bootstrap(): Promise<INestApplication> {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||
bufferLogs: true,
|
||||
// bodyParser is enabled by default in NestJS
|
||||
rawBody: true, // Enable raw body access for debugging
|
||||
});
|
||||
|
||||
// Configure body parser limits via Express adapter
|
||||
app.useBodyParser("json", { limit: "10mb" });
|
||||
app.useBodyParser("urlencoded", { extended: true, limit: "10mb" });
|
||||
|
||||
// Set Pino as the logger
|
||||
app.useLogger(app.get(Logger));
|
||||
|
||||
@ -63,10 +66,6 @@ export async function bootstrap(): Promise<INestApplication> {
|
||||
expressInstance.disable("x-powered-by");
|
||||
}
|
||||
|
||||
// Configure JSON body parser with proper limits
|
||||
app.use(express.json({ limit: "10mb" }));
|
||||
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
||||
|
||||
// Enhanced cookie parser with security options
|
||||
app.use(cookieParser());
|
||||
|
||||
|
||||
2
apps/portal/next-env.d.ts
vendored
2
apps/portal/next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@ -1,122 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="720" viewBox="0 0 1200 720">
|
||||
<defs>
|
||||
<style>
|
||||
.lane { fill: #f7f9fc; stroke: #d0d7de; }
|
||||
.lane-header { fill: #e6eef8; stroke: #b6c6d6; font: 600 16px sans-serif; }
|
||||
.step { fill: #ffffff; stroke: #94a3b8; rx: 8; }
|
||||
.step-text { font: 12px sans-serif; fill: #0f172a; }
|
||||
.label { font: 12px sans-serif; fill: #334155; }
|
||||
.arrow { stroke: #475569; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
|
||||
.num { fill: #2563eb; font: 600 12px sans-serif; }
|
||||
</style>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#475569" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Lanes -->
|
||||
<rect x="30" y="20" width="260" height="680" class="lane"/>
|
||||
<rect x="330" y="20" width="260" height="680" class="lane"/>
|
||||
<rect x="630" y="20" width="260" height="680" class="lane"/>
|
||||
<rect x="930" y="20" width="260" height="680" class="lane"/>
|
||||
|
||||
<!-- Headers -->
|
||||
<rect x="30" y="20" width="260" height="40" class="lane-header"/>
|
||||
<rect x="330" y="20" width="260" height="40" class="lane-header"/>
|
||||
<rect x="630" y="20" width="260" height="40" class="lane-header"/>
|
||||
<rect x="930" y="20" width="260" height="40" class="lane-header"/>
|
||||
<text x="160" y="46" text-anchor="middle" class="lane-header">Customer</text>
|
||||
<text x="460" y="46" text-anchor="middle" class="lane-header">Portal (BFF)</text>
|
||||
<text x="760" y="46" text-anchor="middle" class="lane-header">Salesforce</text>
|
||||
<text x="1060" y="46" text-anchor="middle" class="lane-header">WHMCS</text>
|
||||
|
||||
<!-- Steps: Customer -->
|
||||
<rect x="50" y="90" width="220" height="56" class="step"/>
|
||||
<text x="60" y="110" class="step-text">1) Sign Up & Link Account</text>
|
||||
<text x="60" y="126" class="step-text">Provide Customer Number</text>
|
||||
|
||||
<rect x="50" y="170" width="220" height="56" class="step"/>
|
||||
<text x="60" y="190" class="step-text">2) Add Payment Method</text>
|
||||
<text x="60" y="206" class="step-text">Secure SSO to WHMCS</text>
|
||||
|
||||
<rect x="50" y="250" width="220" height="56" class="step"/>
|
||||
<text x="60" y="270" class="step-text">3) Browse Catalog</text>
|
||||
<text x="60" y="286" class="step-text">Select Plan & Add‑ons</text>
|
||||
|
||||
<rect x="50" y="510" width="220" height="56" class="step"/>
|
||||
<text x="60" y="530" class="step-text">7) View Invoices</text>
|
||||
<text x="60" y="546" class="step-text">Pay via SSO</text>
|
||||
|
||||
<!-- Steps: Portal -->
|
||||
<rect x="350" y="120" width="220" height="56" class="step"/>
|
||||
<text x="360" y="140" class="step-text">Validate mapping & address</text>
|
||||
<text x="360" y="156" class="step-text">Create WHMCS client</text>
|
||||
|
||||
<rect x="350" y="200" width="220" height="56" class="step"/>
|
||||
<text x="360" y="220" class="step-text">Open WHMCS payment page</text>
|
||||
<text x="360" y="236" class="step-text">(SSO)</text>
|
||||
|
||||
<rect x="350" y="300" width="220" height="56" class="step"/>
|
||||
<text x="360" y="320" class="step-text">Create Order in SF</text>
|
||||
<text x="360" y="336" class="step-text">(snapshot address)</text>
|
||||
|
||||
<rect x="350" y="380" width="220" height="56" class="step"/>
|
||||
<text x="360" y="400" class="step-text">Provision in WHMCS</text>
|
||||
<text x="360" y="416" class="step-text">after approval</text>
|
||||
|
||||
<rect x="350" y="510" width="220" height="56" class="step"/>
|
||||
<text x="360" y="530" class="step-text">Show subs & invoices</text>
|
||||
|
||||
<!-- Steps: Salesforce -->
|
||||
<rect x="650" y="300" width="220" height="56" class="step"/>
|
||||
<text x="660" y="320" class="step-text">Review & Approve</text>
|
||||
<text x="660" y="336" class="step-text">Order</text>
|
||||
|
||||
<!-- Steps: WHMCS -->
|
||||
<rect x="950" y="170" width="220" height="56" class="step"/>
|
||||
<text x="960" y="190" class="step-text">Store Payment Methods</text>
|
||||
|
||||
<rect x="950" y="380" width="220" height="56" class="step"/>
|
||||
<text x="960" y="400" class="step-text">Create Services & Invoice</text>
|
||||
|
||||
<!-- Arrows + labels -->
|
||||
<!-- Signup -> Portal create WHMCS client -->
|
||||
<path d="M270 118 C 300 118, 320 118, 350 118" class="arrow"/>
|
||||
<text x="310" y="110" class="label" text-anchor="middle">Customer Number</text>
|
||||
|
||||
<!-- Add payment -> Portal -> WHMCS -->
|
||||
<path d="M270 198 C 300 198, 320 198, 350 198" class="arrow"/>
|
||||
<path d="M570 228 C 600 228, 900 198, 950 198" class="arrow"/>
|
||||
<text x="760" y="188" class="label" text-anchor="middle">SSO Payment Page</text>
|
||||
|
||||
<!-- Browse -> Create Order (Portal) -->
|
||||
<path d="M270 278 C 300 278, 320 318, 350 318" class="arrow"/>
|
||||
<text x="310" y="300" class="label" text-anchor="middle">Selected items</text>
|
||||
|
||||
<!-- Portal -> SF (create order) -->
|
||||
<path d="M570 328 C 600 328, 620 328, 650 328" class="arrow"/>
|
||||
<text x="610" y="320" class="label" text-anchor="middle">Order + address</text>
|
||||
|
||||
<!-- SF approve -> Portal provision -->
|
||||
<path d="M760 356 C 760 390, 570 408, 570 408" class="arrow"/>
|
||||
<text x="680" y="388" class="label">Approved</text>
|
||||
|
||||
<!-- Portal provision -> WHMCS -->
|
||||
<path d="M570 408 C 770 408, 900 408, 950 408" class="arrow"/>
|
||||
<text x="760" y="398" class="label" text-anchor="middle">AddOrder + Accept</text>
|
||||
|
||||
<!-- WHMCS -> Portal (subs & invoices) -->
|
||||
<path d="M950 538 C 900 538, 600 538, 570 538" class="arrow"/>
|
||||
<text x="760" y="528" class="label" text-anchor="middle">Subscriptions + Invoices</text>
|
||||
|
||||
<!-- Portal -> Customer (display) -->
|
||||
<path d="M350 538 C 320 538, 300 538, 270 538" class="arrow"/>
|
||||
|
||||
<!-- Portal -> WHMCS (pay SSO) -->
|
||||
<path d="M350 566 C 600 566, 900 566, 950 566" class="arrow"/>
|
||||
<text x="760" y="560" class="label" text-anchor="middle">Pay Invoice (SSO)</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<text x="30" y="700" class="label">Legend: SSO = secure single sign-on; SF = Salesforce</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
@ -150,15 +150,6 @@ Use these slide-ready bullets to explain, in plain language, how our portal work
|
||||
|
||||
Tip: Pair these slides with a simple swimlane diagram (Customer, Portal, Salesforce, WHMCS) showing the hand‑offs at order and activation.
|
||||
|
||||
References for deeper reading (optional for presenters)
|
||||
|
||||
- Address flow details: docs/ADDRESS_SYSTEM.md
|
||||
- Technical overview: docs/PORTAL-INTEGRATION-OVERVIEW.md
|
||||
|
||||
Diagram (PNG/SVG for slides)
|
||||
|
||||
- Swimlane visual: docs/assets/portal-swimlane.svg
|
||||
|
||||
---
|
||||
|
||||
## 11) Migration (Moving Existing WHMCS Users Into the Portal)
|
||||
|
||||
@ -58,7 +58,6 @@
|
||||
"globals": "^16.5.0",
|
||||
"husky": "^9.1.7",
|
||||
"prettier": "^3.7.4",
|
||||
"sharp": "^0.33.5",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.49.0"
|
||||
|
||||
242
pnpm-lock.yaml
generated
242
pnpm-lock.yaml
generated
@ -43,9 +43,6 @@ importers:
|
||||
prettier:
|
||||
specifier: ^3.7.4
|
||||
version: 3.7.4
|
||||
sharp:
|
||||
specifier: ^0.33.5
|
||||
version: 0.33.5
|
||||
tsx:
|
||||
specifier: ^4.21.0
|
||||
version: 4.21.0
|
||||
@ -775,65 +772,33 @@ packages:
|
||||
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@img/sharp-darwin-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-x64@0.33.5':
|
||||
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||
cpu: [arm]
|
||||
@ -849,64 +814,32 @@ packages:
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
||||
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm@0.33.5':
|
||||
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -925,59 +858,30 @@ packages:
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-s390x@0.33.5':
|
||||
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-s390x@0.34.5':
|
||||
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-x64@0.33.5':
|
||||
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-wasm32@0.33.5':
|
||||
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@img/sharp-wasm32@0.34.5':
|
||||
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -989,24 +893,12 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-ia32@0.33.5':
|
||||
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-ia32@0.34.5':
|
||||
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-x64@0.33.5':
|
||||
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -2836,13 +2728,6 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
color-string@1.9.1:
|
||||
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
|
||||
|
||||
color@4.2.3:
|
||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||
engines: {node: '>=12.5.0'}
|
||||
|
||||
colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
|
||||
@ -3828,9 +3713,6 @@ packages:
|
||||
is-arrayish@0.2.1:
|
||||
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
|
||||
|
||||
is-arrayish@0.3.4:
|
||||
resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
|
||||
|
||||
is-async-function@2.1.1:
|
||||
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -5265,10 +5147,6 @@ packages:
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
sharp@0.33.5:
|
||||
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
|
||||
sharp@0.34.5:
|
||||
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -5304,9 +5182,6 @@ packages:
|
||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
simple-swizzle@0.2.4:
|
||||
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
||||
|
||||
sirv@2.0.4:
|
||||
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
|
||||
engines: {node: '>= 10'}
|
||||
@ -6449,47 +6324,25 @@ snapshots:
|
||||
'@img/colour@1.0.0':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-arm64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-x64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
optional: true
|
||||
|
||||
@ -6499,45 +6352,23 @@ snapshots:
|
||||
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.0.5
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||
@ -6553,51 +6384,26 @@ snapshots:
|
||||
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-s390x@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-s390x': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-s390x@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-x64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-wasm32@0.33.5':
|
||||
dependencies:
|
||||
'@emnapi/runtime': 1.7.1
|
||||
optional: true
|
||||
|
||||
'@img/sharp-wasm32@0.34.5':
|
||||
dependencies:
|
||||
'@emnapi/runtime': 1.7.1
|
||||
@ -6606,15 +6412,9 @@ snapshots:
|
||||
'@img/sharp-win32-arm64@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-ia32@0.33.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-ia32@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-x64@0.33.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
optional: true
|
||||
|
||||
@ -8687,16 +8487,6 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
color-string@1.9.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
simple-swizzle: 0.2.4
|
||||
|
||||
color@4.2.3:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
color-string: 1.9.1
|
||||
|
||||
colorette@2.0.20: {}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
@ -9891,8 +9681,6 @@ snapshots:
|
||||
|
||||
is-arrayish@0.2.1: {}
|
||||
|
||||
is-arrayish@0.3.4: {}
|
||||
|
||||
is-async-function@2.1.1:
|
||||
dependencies:
|
||||
async-function: 1.0.0
|
||||
@ -11531,32 +11319,6 @@ snapshots:
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
sharp@0.33.5:
|
||||
dependencies:
|
||||
color: 4.2.3
|
||||
detect-libc: 2.1.2
|
||||
semver: 7.7.3
|
||||
optionalDependencies:
|
||||
'@img/sharp-darwin-arm64': 0.33.5
|
||||
'@img/sharp-darwin-x64': 0.33.5
|
||||
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
||||
'@img/sharp-libvips-darwin-x64': 1.0.4
|
||||
'@img/sharp-libvips-linux-arm': 1.0.5
|
||||
'@img/sharp-libvips-linux-arm64': 1.0.4
|
||||
'@img/sharp-libvips-linux-s390x': 1.0.4
|
||||
'@img/sharp-libvips-linux-x64': 1.0.4
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
||||
'@img/sharp-linux-arm': 0.33.5
|
||||
'@img/sharp-linux-arm64': 0.33.5
|
||||
'@img/sharp-linux-s390x': 0.33.5
|
||||
'@img/sharp-linux-x64': 0.33.5
|
||||
'@img/sharp-linuxmusl-arm64': 0.33.5
|
||||
'@img/sharp-linuxmusl-x64': 0.33.5
|
||||
'@img/sharp-wasm32': 0.33.5
|
||||
'@img/sharp-win32-ia32': 0.33.5
|
||||
'@img/sharp-win32-x64': 0.33.5
|
||||
|
||||
sharp@0.34.5:
|
||||
dependencies:
|
||||
'@img/colour': 1.0.0
|
||||
@ -11627,10 +11389,6 @@ snapshots:
|
||||
|
||||
signal-exit@4.1.0: {}
|
||||
|
||||
simple-swizzle@0.2.4:
|
||||
dependencies:
|
||||
is-arrayish: 0.3.4
|
||||
|
||||
sirv@2.0.4:
|
||||
dependencies:
|
||||
'@polka/url': 1.0.0-next.29
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Simple SVG -> PNG converter using sharp.
|
||||
* Usage: node svg2png.mjs <input.svg> <output.png> <width> <height>
|
||||
*/
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import sharp from "sharp";
|
||||
|
||||
async function main() {
|
||||
const [, , inPath, outPath, wArg, hArg] = process.argv;
|
||||
if (!inPath || !outPath || !wArg || !hArg) {
|
||||
console.error("Usage: node svg2png.mjs <input.svg> <output.png> <width> <height>");
|
||||
process.exit(1);
|
||||
}
|
||||
const width = Number(wArg);
|
||||
const height = Number(hArg);
|
||||
if (!width || !height) {
|
||||
console.error("Width and height must be numbers");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const absIn = path.resolve(inPath);
|
||||
const absOut = path.resolve(outPath);
|
||||
const svg = await fs.readFile(absIn);
|
||||
|
||||
// Render with background white to avoid transparency issues in slides
|
||||
const png = await sharp(svg, { density: 300 })
|
||||
.resize(width, height, { fit: "contain", background: { r: 255, g: 255, b: 255, alpha: 1 } })
|
||||
.png({ compressionLevel: 9 })
|
||||
.toBuffer();
|
||||
|
||||
await fs.mkdir(path.dirname(absOut), { recursive: true });
|
||||
await fs.writeFile(absOut, png);
|
||||
console.log(`Wrote ${absOut} (${width}x${height})`);
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error("svg2png failed:", err?.message || err);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user