var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
  if (typeof require !== "undefined") return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};

// shared/schema-cart.ts
import {
  boolean,
  decimal,
  index,
  integer,
  pgTable,
  serial,
  text,
  timestamp,
  varchar
} from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
var products, cartItems, cartItemsRelations, orders, orderItems, ordersRelations, orderItemsRelations, downloads, downloadsRelations;
var init_schema_cart = __esm({
  "shared/schema-cart.ts"() {
    "use strict";
    init_schema();
    products = pgTable("products", {
      id: varchar("id").primaryKey(),
      // e.g., "digital-full", "print-8x10"
      name: varchar("name").notNull(),
      description: text("description"),
      price: decimal("price", { precision: 10, scale: 2 }).notNull().default("0"),
      type: varchar("type").notNull(),
      // "digital", "print", "canvas", "framed"
      size: varchar("size"),
      active: boolean("active").notNull().default(true),
      createdAt: timestamp("created_at").defaultNow(),
      updatedAt: timestamp("updated_at").defaultNow()
    });
    cartItems = pgTable("cart_items", {
      id: serial("id").primaryKey(),
      userId: varchar("user_id").notNull().references(() => users.id),
      imageId: integer("image_id").notNull().references(() => images.id),
      productId: varchar("product_id").notNull().references(() => products.id),
      price: decimal("price", { precision: 10, scale: 2 }).notNull(),
      addedAt: timestamp("added_at").defaultNow()
    }, (table) => {
      return {
        userIdIdx: index("cart_user_id_idx").on(table.userId),
        uniqueItem: index("cart_unique_item_idx").on(table.userId, table.imageId, table.productId)
      };
    });
    cartItemsRelations = relations(cartItems, ({ one }) => ({
      user: one(users, {
        fields: [cartItems.userId],
        references: [users.id]
      }),
      image: one(images, {
        fields: [cartItems.imageId],
        references: [images.id]
      }),
      product: one(products, {
        fields: [cartItems.productId],
        references: [products.id]
      })
    }));
    orders = pgTable("orders", {
      id: serial("id").primaryKey(),
      userId: varchar("user_id").notNull().references(() => users.id),
      orderNumber: varchar("order_number").notNull().unique(),
      status: varchar("status").notNull().default("pending"),
      // pending, processing, completed, failed
      totalAmount: decimal("total_amount", { precision: 10, scale: 2 }).notNull(),
      paymentIntentId: varchar("payment_intent_id"),
      createdAt: timestamp("created_at").defaultNow(),
      updatedAt: timestamp("updated_at").defaultNow()
    }, (table) => {
      return {
        userIdIdx: index("order_user_id_idx").on(table.userId)
      };
    });
    orderItems = pgTable("order_items", {
      id: serial("id").primaryKey(),
      orderId: integer("order_id").notNull().references(() => orders.id),
      imageId: integer("image_id").notNull().references(() => images.id),
      productId: varchar("product_id").notNull().references(() => products.id),
      price: decimal("price", { precision: 10, scale: 2 }).notNull()
    }, (table) => {
      return {
        orderIdIdx: index("order_item_order_id_idx").on(table.orderId)
      };
    });
    ordersRelations = relations(orders, ({ one, many }) => ({
      user: one(users, {
        fields: [orders.userId],
        references: [users.id]
      }),
      items: many(orderItems)
    }));
    orderItemsRelations = relations(orderItems, ({ one }) => ({
      order: one(orders, {
        fields: [orderItems.orderId],
        references: [orders.id]
      }),
      image: one(images, {
        fields: [orderItems.imageId],
        references: [images.id]
      }),
      product: one(products, {
        fields: [orderItems.productId],
        references: [products.id]
      })
    }));
    downloads = pgTable("downloads", {
      id: serial("id").primaryKey(),
      userId: varchar("user_id").notNull().references(() => users.id),
      orderId: integer("order_id").notNull().references(() => orders.id),
      imageId: integer("image_id").notNull().references(() => images.id),
      productId: varchar("product_id").notNull().references(() => products.id),
      downloadUrl: text("download_url").notNull(),
      downloadCount: integer("download_count").notNull().default(0),
      expiresAt: timestamp("expires_at"),
      createdAt: timestamp("created_at").defaultNow(),
      updatedAt: timestamp("updated_at").defaultNow()
    }, (table) => {
      return {
        userIdIdx: index("download_user_id_idx").on(table.userId),
        orderIdIdx: index("download_order_id_idx").on(table.orderId)
      };
    });
    downloadsRelations = relations(downloads, ({ one }) => ({
      user: one(users, {
        fields: [downloads.userId],
        references: [users.id]
      }),
      order: one(orders, {
        fields: [downloads.orderId],
        references: [orders.id]
      }),
      image: one(images, {
        fields: [downloads.imageId],
        references: [images.id]
      }),
      product: one(products, {
        fields: [downloads.productId],
        references: [products.id]
      })
    }));
  }
});

// shared/schema.ts
var schema_exports = {};
__export(schema_exports, {
  blogCategories: () => blogCategories,
  blogMedia: () => blogMedia,
  blogPostCategories: () => blogPostCategories,
  blogPostJobs: () => blogPostJobs,
  blogPostJobsRelations: () => blogPostJobsRelations,
  blogPosts: () => blogPosts,
  bookings: () => bookings,
  cartItems: () => cartItems,
  cartItemsRelations: () => cartItemsRelations,
  contactMessages: () => contactMessages,
  downloads: () => downloads,
  downloadsRelations: () => downloadsRelations,
  emailLogs: () => emailLogs,
  emailTemplates: () => emailTemplates,
  imageCategories: () => imageCategories,
  imageComments: () => imageComments,
  imageFavorites: () => imageFavorites,
  images: () => images,
  insertBlogCategorySchema: () => insertBlogCategorySchema,
  insertBlogMediaSchema: () => insertBlogMediaSchema,
  insertBlogPostJobSchema: () => insertBlogPostJobSchema,
  insertBlogPostSchema: () => insertBlogPostSchema,
  insertBookingSchema: () => insertBookingSchema,
  insertContactMessageSchema: () => insertContactMessageSchema,
  insertEmailLogSchema: () => insertEmailLogSchema,
  insertEmailTemplateSchema: () => insertEmailTemplateSchema,
  insertImageCommentSchema: () => insertImageCommentSchema,
  insertImageFavoriteSchema: () => insertImageFavoriteSchema,
  insertImageSchema: () => insertImageSchema,
  insertJobMemberSchema: () => insertJobMemberSchema,
  insertJobSchema: () => insertJobSchema,
  insertMoodBoardCollaboratorSchema: () => insertMoodBoardCollaboratorSchema,
  insertMoodBoardCommentSchema: () => insertMoodBoardCommentSchema,
  insertMoodBoardItemSchema: () => insertMoodBoardItemSchema,
  insertMoodBoardSchema: () => insertMoodBoardSchema,
  insertPaymentTransactionSchema: () => insertPaymentTransactionSchema,
  insertPortfolioCategorySchema: () => insertPortfolioCategorySchema,
  insertReferralSchema: () => insertReferralSchema,
  insertReviewImageSchema: () => insertReviewImageSchema,
  insertReviewSchema: () => insertReviewSchema,
  insertSessionTypeSchema: () => insertSessionTypeSchema,
  insertSocialMediaAccountSchema: () => insertSocialMediaAccountSchema,
  insertUserProfileSchema: () => insertUserProfileSchema,
  insertUserSchema: () => insertUserSchema,
  insertWebsiteContentSchema: () => insertWebsiteContentSchema,
  jobMembers: () => jobMembers,
  jobMembersRelations: () => jobMembersRelations,
  jobs: () => jobs,
  moodBoardCollaborators: () => moodBoardCollaborators,
  moodBoardCollaboratorsRelations: () => moodBoardCollaboratorsRelations,
  moodBoardComments: () => moodBoardComments,
  moodBoardItems: () => moodBoardItems,
  moodBoardItemsRelations: () => moodBoardItemsRelations,
  moodBoardUserPresence: () => moodBoardUserPresence,
  moodBoards: () => moodBoards,
  moodBoardsRelations: () => moodBoardsRelations,
  orderItems: () => orderItems,
  orderItemsRelations: () => orderItemsRelations,
  orders: () => orders,
  ordersRelations: () => ordersRelations,
  paymentTransactions: () => paymentTransactions,
  paymentTransactionsRelations: () => paymentTransactionsRelations,
  portfolioCategories: () => portfolioCategories,
  products: () => products,
  referrals: () => referrals,
  reviewImages: () => reviewImages,
  reviews: () => reviews,
  reviewsRelations: () => reviewsRelations,
  sessionTypes: () => sessionTypes,
  sessions: () => sessions,
  socialMediaAccounts: () => socialMediaAccounts,
  userProfiles: () => userProfiles,
  userProfilesRelations: () => userProfilesRelations,
  users: () => users,
  websiteContent: () => websiteContent
});
import { pgTable as pgTable2, text as text2, serial as serial2, timestamp as timestamp2, boolean as boolean2, integer as integer2, varchar as varchar2, json, primaryKey as primaryKey2, uniqueIndex, real } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { relations as relations2 } from "drizzle-orm";
var sessions, users, insertUserSchema, userProfiles, userProfilesRelations, insertUserProfileSchema, paymentTransactions, paymentTransactionsRelations, insertPaymentTransactionSchema, jobs, insertJobSchema, jobMembers, jobMembersRelations, insertJobMemberSchema, sessionTypes, insertSessionTypeSchema, images, insertImageSchema, imageComments, insertImageCommentSchema, reviews, insertReviewSchema, reviewsRelations, reviewImages, insertReviewImageSchema, referrals, insertReferralSchema, contactMessages, insertContactMessageSchema, portfolioCategories, insertPortfolioCategorySchema, imageCategories, imageFavorites, insertImageFavoriteSchema, blogCategories, insertBlogCategorySchema, blogPosts, insertBlogPostSchema, blogMedia, insertBlogMediaSchema, blogPostCategories, blogPostJobs, emailTemplates, insertEmailTemplateSchema, emailLogs, insertEmailLogSchema, blogPostJobsRelations, insertBlogPostJobSchema, moodBoards, moodBoardItems, moodBoardComments, moodBoardUserPresence, moodBoardCollaborators, moodBoardsRelations, moodBoardItemsRelations, moodBoardCollaboratorsRelations, insertMoodBoardSchema, insertMoodBoardItemSchema, insertMoodBoardCommentSchema, insertMoodBoardCollaboratorSchema, websiteContent, insertWebsiteContentSchema, bookings, insertBookingSchema, socialMediaAccounts, insertSocialMediaAccountSchema;
var init_schema = __esm({
  "shared/schema.ts"() {
    "use strict";
    init_schema_cart();
    sessions = pgTable2(
      "sessions",
      {
        sid: varchar2("sid").primaryKey(),
        sess: json("sess").notNull(),
        expire: timestamp2("expire").notNull()
      },
      (table) => ({
        expireIdx: uniqueIndex("IDX_session_expire").on(table.expire)
      })
    );
    users = pgTable2("users", {
      id: varchar2("id").primaryKey(),
      name: text2("name"),
      email: text2("email").unique(),
      emailVerified: timestamp2("email_verified"),
      password: text2("password"),
      image: text2("image"),
      locale: varchar2("locale", { length: 10 }).default("en"),
      role: varchar2("role", { length: 20 }).default("user").notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertUserSchema = createInsertSchema(users);
    userProfiles = pgTable2("user_profiles", {
      id: serial2("id").primaryKey(),
      userId: varchar2("user_id").references(() => users.id).notNull().unique(),
      firstName: varchar2("first_name", { length: 100 }),
      lastName: varchar2("last_name", { length: 100 }),
      street: text2("street"),
      city: text2("city"),
      state: text2("state"),
      zip: text2("zip"),
      country: text2("country"),
      mobile: text2("mobile"),
      landline: text2("landline"),
      preferredContact: varchar2("preferred_contact", { length: 20 }),
      // "email" | "mobile" | "whatsapp"
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    userProfilesRelations = relations2(userProfiles, ({ one }) => ({
      user: one(users, {
        fields: [userProfiles.userId],
        references: [users.id]
      })
    }));
    insertUserProfileSchema = createInsertSchema(userProfiles).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    paymentTransactions = pgTable2("payment_transactions", {
      id: serial2("id").primaryKey(),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      jobId: integer2("job_id").references(() => jobs.id),
      amount: real("amount").notNull(),
      status: varchar2("status", { length: 50 }).default("pending").notNull(),
      paymentType: varchar2("payment_type", { length: 20 }).notNull(),
      // "deposit" | "balance" | "full"
      paymentMethod: varchar2("payment_method", { length: 20 }).notNull(),
      // "credit_card" | "bank_transfer" | "cash"
      stripePaymentId: varchar2("stripe_payment_id", { length: 255 }),
      stripeClientSecret: varchar2("stripe_client_secret", { length: 255 }),
      metadata: json("metadata"),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    paymentTransactionsRelations = relations2(paymentTransactions, ({ one }) => ({
      user: one(users, {
        fields: [paymentTransactions.userId],
        references: [users.id]
      }),
      job: one(jobs, {
        fields: [paymentTransactions.jobId],
        references: [jobs.id]
      })
    }));
    insertPaymentTransactionSchema = createInsertSchema(paymentTransactions).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    jobs = pgTable2("jobs", {
      id: serial2("id").primaryKey(),
      clientId: varchar2("client_id").references(() => users.id),
      title: varchar2("title", { length: 200 }).notNull(),
      sessionType: text2("session_type").notNull(),
      date: timestamp2("date"),
      status: varchar2("status", { length: 50 }).default("lead").notNull(),
      locale: varchar2("locale", { length: 10 }).default("en").notNull(),
      location: text2("location"),
      notes: text2("notes"),
      referralCode: varchar2("referral_code", { length: 50 }),
      contractUrl: text2("contract_url"),
      contractSigned: boolean2("contract_signed").default(false),
      depositPaid: boolean2("deposit_paid").default(false),
      balancePaid: boolean2("balance_paid").default(false),
      depositAmount: real("deposit_amount"),
      balanceAmount: real("balance_amount"),
      totalAmount: real("total_amount"),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertJobSchema = createInsertSchema(jobs).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    jobMembers = pgTable2("job_members", {
      jobId: integer2("job_id").references(() => jobs.id).notNull(),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      role: varchar2("role", { length: 20 }).default("member").notNull()
      // "owner" | "member"
    }, (table) => ({
      pk: primaryKey2({ columns: [table.jobId, table.userId] })
    }));
    jobMembersRelations = relations2(jobMembers, ({ one }) => ({
      job: one(jobs, {
        fields: [jobMembers.jobId],
        references: [jobs.id]
      }),
      user: one(users, {
        fields: [jobMembers.userId],
        references: [users.id]
      })
    }));
    insertJobMemberSchema = createInsertSchema(jobMembers);
    sessionTypes = pgTable2("session_types", {
      id: serial2("id").primaryKey(),
      name: varchar2("name", { length: 100 }).notNull(),
      description: text2("description"),
      basePrice: real("base_price"),
      durationMinutes: integer2("duration_minutes"),
      active: boolean2("active").default(true),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    insertSessionTypeSchema = createInsertSchema(sessionTypes).omit({
      id: true,
      createdAt: true
    });
    images = pgTable2("images", {
      id: serial2("id").primaryKey(),
      jobId: integer2("job_id").references(() => jobs.id),
      title: varchar2("title", { length: 200 }),
      urlOriginal: text2("url_original").notNull(),
      urlPreview: text2("url_preview"),
      urlWatermark: text2("url_watermark"),
      inPortfolio: boolean2("in_portfolio").default(false),
      favorite: boolean2("favorite").default(false),
      publicId: text2("public_id"),
      metadata: json("metadata"),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertImageSchema = createInsertSchema(images).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    imageComments = pgTable2("image_comments", {
      id: serial2("id").primaryKey(),
      imageId: integer2("image_id").references(() => images.id).notNull(),
      userId: varchar2("user_id").references(() => users.id),
      comment: text2("comment").notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    insertImageCommentSchema = createInsertSchema(imageComments).omit({
      id: true,
      createdAt: true
    });
    reviews = pgTable2("reviews", {
      id: serial2("id").primaryKey(),
      jobId: integer2("job_id").references(() => jobs.id).notNull(),
      userId: varchar2("user_id").references(() => users.id),
      stars: integer2("stars").notNull(),
      text: text2("text"),
      name: text2("name"),
      email: text2("email"),
      published: boolean2("published").default(false),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    insertReviewSchema = createInsertSchema(reviews).omit({
      id: true,
      createdAt: true
    });
    reviewsRelations = relations2(reviews, ({ one }) => ({
      job: one(jobs, {
        fields: [reviews.jobId],
        references: [jobs.id]
      }),
      user: one(users, {
        fields: [reviews.userId],
        references: [users.id]
      })
    }));
    reviewImages = pgTable2("review_images", {
      reviewId: integer2("review_id").references(() => reviews.id).notNull(),
      imageId: integer2("image_id").references(() => images.id).notNull()
    }, (table) => ({
      pk: primaryKey2({ columns: [table.reviewId, table.imageId] })
    }));
    insertReviewImageSchema = createInsertSchema(reviewImages);
    referrals = pgTable2("referrals", {
      code: varchar2("code", { length: 50 }).primaryKey(),
      ownerUserId: varchar2("owner_user_id").references(() => users.id).notNull(),
      redeemedByUserId: varchar2("redeemed_by_user_id").references(() => users.id),
      redeemedAt: timestamp2("redeemed_at"),
      discount: real("discount"),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    insertReferralSchema = createInsertSchema(referrals).omit({
      createdAt: true
    });
    contactMessages = pgTable2("contact_messages", {
      id: serial2("id").primaryKey(),
      name: text2("name").notNull(),
      email: text2("email").notNull(),
      phone: text2("phone"),
      service: text2("service").notNull(),
      message: text2("message").notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      read: boolean2("read").default(false).notNull()
    });
    insertContactMessageSchema = createInsertSchema(contactMessages).omit({
      id: true,
      createdAt: true,
      read: true
    });
    portfolioCategories = pgTable2("portfolio_categories", {
      id: serial2("id").primaryKey(),
      name: varchar2("name", { length: 100 }).notNull(),
      slug: varchar2("slug", { length: 100 }).notNull().unique(),
      description: text2("description"),
      active: boolean2("active").default(true),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    insertPortfolioCategorySchema = createInsertSchema(portfolioCategories).omit({
      id: true,
      createdAt: true
    });
    imageCategories = pgTable2("image_categories", {
      imageId: integer2("image_id").references(() => images.id).notNull(),
      categoryId: integer2("category_id").references(() => portfolioCategories.id).notNull()
    }, (table) => ({
      pk: primaryKey2({ columns: [table.imageId, table.categoryId] })
    }));
    imageFavorites = pgTable2("image_favorites", {
      id: serial2("id").primaryKey(),
      imageId: integer2("image_id").references(() => images.id).notNull(),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    }, (table) => ({
      uniqueConstraint: uniqueIndex("unique_image_user_favorite").on(table.imageId, table.userId)
    }));
    insertImageFavoriteSchema = createInsertSchema(imageFavorites).omit({
      id: true,
      createdAt: true
    });
    blogCategories = pgTable2("blog_categories", {
      id: serial2("id").primaryKey(),
      name: varchar2("name", { length: 100 }).notNull(),
      slug: varchar2("slug", { length: 100 }).notNull().unique(),
      description: text2("description"),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    insertBlogCategorySchema = createInsertSchema(blogCategories).omit({
      id: true,
      createdAt: true
    });
    blogPosts = pgTable2("blog_posts", {
      id: serial2("id").primaryKey(),
      title: varchar2("title", { length: 255 }).notNull(),
      slug: varchar2("slug", { length: 255 }).notNull().unique(),
      content: json("content").notNull(),
      excerpt: text2("excerpt"),
      featuredImageUrl: text2("featured_image_url"),
      locales: text2("locales").array(),
      published: boolean2("published").default(false),
      publishedAt: timestamp2("published_at"),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertBlogPostSchema = createInsertSchema(blogPosts).omit({
      id: true,
      createdAt: true,
      updatedAt: true,
      publishedAt: true
    });
    blogMedia = pgTable2("blog_media", {
      id: serial2("id").primaryKey(),
      url: text2("url").notNull(),
      type: varchar2("type", { length: 20 }).notNull(),
      // "image" | "video"
      blogPostId: integer2("blog_post_id").references(() => blogPosts.id).notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertBlogMediaSchema = createInsertSchema(blogMedia).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    blogPostCategories = pgTable2("blog_post_categories", {
      blogPostId: integer2("blog_post_id").references(() => blogPosts.id).notNull(),
      blogCategoryId: integer2("blog_category_id").references(() => blogCategories.id).notNull()
    }, (table) => ({
      pk: primaryKey2({ columns: [table.blogPostId, table.blogCategoryId] })
    }));
    blogPostJobs = pgTable2("blog_post_jobs", {
      blogPostId: integer2("blog_post_id").references(() => blogPosts.id).notNull(),
      jobId: integer2("job_id").references(() => jobs.id).notNull()
    }, (table) => ({
      pk: primaryKey2({ columns: [table.blogPostId, table.jobId] })
    }));
    emailTemplates = pgTable2("email_templates", {
      id: serial2("id").primaryKey(),
      name: varchar2("name", { length: 100 }).notNull(),
      subject: varchar2("subject", { length: 255 }).notNull(),
      body: text2("body").notNull(),
      locale: varchar2("locale", { length: 10 }).default("en").notNull(),
      category: varchar2("category", { length: 50 }).notNull(),
      // "workflow", "marketing", "client", etc.
      workflowStatus: varchar2("workflow_status", { length: 50 }),
      // The status this template is associated with, if any
      active: boolean2("active").default(true).notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertEmailTemplateSchema = createInsertSchema(emailTemplates).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    emailLogs = pgTable2("email_logs", {
      id: serial2("id").primaryKey(),
      jobId: integer2("job_id").references(() => jobs.id),
      templateId: integer2("template_id").references(() => emailTemplates.id),
      toEmail: varchar2("to_email", { length: 255 }).notNull(),
      fromEmail: varchar2("from_email", { length: 255 }).notNull(),
      subject: varchar2("subject", { length: 255 }).notNull(),
      body: text2("body").notNull(),
      status: varchar2("status", { length: 50 }).default("sent").notNull(),
      // "sent", "failed", "bounced", etc.
      sentAt: timestamp2("sent_at").defaultNow().notNull(),
      metadata: json("metadata")
    });
    insertEmailLogSchema = createInsertSchema(emailLogs).omit({
      id: true,
      sentAt: true
    });
    blogPostJobsRelations = relations2(blogPostJobs, ({ one }) => ({
      blogPost: one(blogPosts, {
        fields: [blogPostJobs.blogPostId],
        references: [blogPosts.id]
      }),
      job: one(jobs, {
        fields: [blogPostJobs.jobId],
        references: [jobs.id]
      })
    }));
    insertBlogPostJobSchema = createInsertSchema(blogPostJobs);
    moodBoards = pgTable2("mood_boards", {
      id: serial2("id").primaryKey(),
      name: varchar2("name", { length: 255 }).notNull(),
      description: text2("description"),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      jobId: integer2("job_id").references(() => jobs.id),
      isCollaborative: boolean2("is_collaborative").default(false),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    moodBoardItems = pgTable2("mood_board_items", {
      id: serial2("id").primaryKey(),
      moodBoardId: integer2("mood_board_id").references(() => moodBoards.id).notNull(),
      title: varchar2("title", { length: 200 }),
      description: text2("description"),
      imageUrl: text2("image_url"),
      externalUrl: text2("external_url"),
      position: integer2("position").default(0),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    moodBoardComments = pgTable2("mood_board_comments", {
      id: serial2("id").primaryKey(),
      moodBoardId: integer2("mood_board_id").references(() => moodBoards.id).notNull(),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      text: text2("text").notNull(),
      createdAt: timestamp2("created_at").defaultNow().notNull()
    });
    moodBoardUserPresence = pgTable2("mood_board_user_presence", {
      moodBoardId: integer2("mood_board_id").references(() => moodBoards.id).notNull(),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      lastActive: timestamp2("last_active").defaultNow().notNull(),
      isTyping: boolean2("is_typing").default(false)
    }, (table) => ({
      pk: primaryKey2({ columns: [table.moodBoardId, table.userId] })
    }));
    moodBoardCollaborators = pgTable2("mood_board_collaborators", {
      moodBoardId: integer2("mood_board_id").references(() => moodBoards.id).notNull(),
      userId: varchar2("user_id").references(() => users.id).notNull(),
      permission: varchar2("permission", { length: 20 }).default("view").notNull(),
      // "view" | "edit"
      createdAt: timestamp2("created_at").defaultNow().notNull()
    }, (table) => ({
      pk: primaryKey2({ columns: [table.moodBoardId, table.userId] })
    }));
    moodBoardsRelations = relations2(moodBoards, ({ one, many }) => ({
      user: one(users, {
        fields: [moodBoards.userId],
        references: [users.id]
      }),
      job: one(jobs, {
        fields: [moodBoards.jobId],
        references: [jobs.id]
      }),
      comments: many(moodBoardComments),
      userPresence: many(moodBoardUserPresence),
      items: many(moodBoardItems),
      collaborators: many(moodBoardCollaborators)
    }));
    moodBoardItemsRelations = relations2(moodBoardItems, ({ one }) => ({
      moodBoard: one(moodBoards, {
        fields: [moodBoardItems.moodBoardId],
        references: [moodBoards.id]
      })
    }));
    moodBoardCollaboratorsRelations = relations2(moodBoardCollaborators, ({ one }) => ({
      moodBoard: one(moodBoards, {
        fields: [moodBoardCollaborators.moodBoardId],
        references: [moodBoards.id]
      }),
      user: one(users, {
        fields: [moodBoardCollaborators.userId],
        references: [users.id]
      })
    }));
    insertMoodBoardSchema = createInsertSchema(moodBoards).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    insertMoodBoardItemSchema = createInsertSchema(moodBoardItems).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    insertMoodBoardCommentSchema = createInsertSchema(moodBoardComments).omit({
      id: true,
      createdAt: true
    });
    insertMoodBoardCollaboratorSchema = createInsertSchema(moodBoardCollaborators).omit({
      createdAt: true
    });
    websiteContent = pgTable2("website_content", {
      id: serial2("id").primaryKey(),
      name: varchar2("name", { length: 255 }).notNull(),
      key: varchar2("key", { length: 255 }).notNull().unique(),
      type: varchar2("type", { length: 20 }).notNull(),
      value_en: text2("value_en"),
      value_pt: text2("value_pt"),
      value_es: text2("value_es"),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertWebsiteContentSchema = createInsertSchema(websiteContent).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    bookings = pgTable2("bookings", {
      id: serial2("id").primaryKey(),
      userId: varchar2("user_id").references(() => users.id),
      sessionTypeId: integer2("session_type_id").references(() => sessionTypes.id).notNull(),
      date: timestamp2("date").notNull(),
      status: varchar2("status", { length: 50 }).default("pending").notNull(),
      name: varchar2("name", { length: 255 }),
      email: varchar2("email", { length: 255 }),
      phone: varchar2("phone", { length: 50 }),
      numberOfPeople: integer2("number_of_people").default(1),
      howHeard: varchar2("how_heard", { length: 100 }),
      alternateDate: timestamp2("alternate_date"),
      location: text2("location"),
      notes: text2("notes"),
      referralCode: varchar2("referral_code", { length: 50 }),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertBookingSchema = createInsertSchema(bookings).omit({
      id: true,
      createdAt: true,
      updatedAt: true
    });
    socialMediaAccounts = pgTable2("social_media_accounts", {
      id: serial2("id").primaryKey(),
      platform: varchar2("platform", { length: 50 }).notNull(),
      // facebook, instagram, linkedin, etc.
      name: varchar2("name", { length: 255 }).notNull(),
      accessToken: text2("access_token").notNull(),
      pageId: varchar2("page_id", { length: 255 }),
      accountId: varchar2("account_id", { length: 255 }),
      active: boolean2("active").default(true),
      lastPostAt: timestamp2("last_post_at"),
      postCount: integer2("post_count").default(0),
      createdAt: timestamp2("created_at").defaultNow().notNull(),
      updatedAt: timestamp2("updated_at").defaultNow().notNull()
    });
    insertSocialMediaAccountSchema = createInsertSchema(socialMediaAccounts).omit({
      id: true,
      createdAt: true,
      updatedAt: true,
      lastPostAt: true,
      postCount: true
    });
  }
});

// server/db.ts
import { Pool, neonConfig } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
import ws from "ws";
var pool, db;
var init_db = __esm({
  "server/db.ts"() {
    "use strict";
    init_schema();
    neonConfig.webSocketConstructor = ws;
    if (!process.env.DATABASE_URL) {
      throw new Error(
        "DATABASE_URL must be set. Did you forget to provision a database?"
      );
    }
    pool = new Pool({ connectionString: process.env.DATABASE_URL });
    db = drizzle({ client: pool, schema: schema_exports });
  }
});

// server/storage.ts
import { eq, desc, and, inArray, sql } from "drizzle-orm";
var DatabaseStorage, storage;
var init_storage = __esm({
  "server/storage.ts"() {
    "use strict";
    init_schema();
    init_db();
    DatabaseStorage = class {
      // User operations
      async getUser(id) {
        const [user] = await db.select().from(users).where(eq(users.id, id));
        return user;
      }
      async getUserByEmail(email) {
        const [user] = await db.select().from(users).where(eq(users.email, email));
        return user;
      }
      async createUser(user) {
        const [newUser] = await db.insert(users).values({
          ...user,
          createdAt: /* @__PURE__ */ new Date(),
          updatedAt: /* @__PURE__ */ new Date()
        }).returning();
        return newUser;
      }
      async updateUser(id, data) {
        const [updatedUser] = await db.update(users).set(data).where(eq(users.id, id)).returning();
        return updatedUser;
      }
      // Job operations
      async createJob(job) {
        const [newJob] = await db.insert(jobs).values(job).returning();
        return newJob;
      }
      async getJob(id) {
        const [job] = await db.select().from(jobs).where(eq(jobs.id, id));
        return job;
      }
      async getJobsByClientId(clientId) {
        return await db.select().from(jobs).where(eq(jobs.clientId, clientId)).orderBy(desc(jobs.createdAt));
      }
      async getJobsByStatus(status) {
        return await db.select().from(jobs).where(eq(jobs.status, status)).orderBy(desc(jobs.createdAt));
      }
      async updateJobStatus(id, status) {
        const [updatedJob] = await db.update(jobs).set({ status }).where(eq(jobs.id, id)).returning();
        return updatedJob;
      }
      async updateJob(id, data) {
        const [updatedJob] = await db.update(jobs).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(eq(jobs.id, id)).returning();
        return updatedJob;
      }
      // Session types
      async createSessionType(sessionType) {
        const [newSessionType] = await db.insert(sessionTypes).values(sessionType).returning();
        return newSessionType;
      }
      async getAllSessionTypes() {
        return await db.select().from(sessionTypes).orderBy(sessionTypes.name);
      }
      async getActiveSessionTypes() {
        return await db.select().from(sessionTypes).where(eq(sessionTypes.active, true)).orderBy(sessionTypes.name);
      }
      // Image operations
      async createImage(image) {
        const [newImage] = await db.insert(images).values(image).returning();
        return newImage;
      }
      async getImage(id) {
        const [image] = await db.select().from(images).where(eq(images.id, id));
        return image;
      }
      async getImagesByJobId(jobId) {
        return await db.select().from(images).where(eq(images.jobId, jobId));
      }
      async getPortfolioImages() {
        return await db.select().from(images).where(eq(images.inPortfolio, true));
      }
      async getPortfolioImagesByCategory(categoryId) {
        const result = await db.select({ image: images }).from(images).innerJoin(imageCategories, eq(images.id, imageCategories.imageId)).where(
          and(
            eq(images.inPortfolio, true),
            eq(imageCategories.categoryId, categoryId)
          )
        );
        return result.map((r) => r.image);
      }
      async updateImage(id, data) {
        const [updatedImage] = await db.update(images).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(eq(images.id, id)).returning();
        return updatedImage;
      }
      async toggleImagePortfolioStatus(id) {
        const [image] = await db.select().from(images).where(eq(images.id, id));
        if (!image) return void 0;
        const [updatedImage] = await db.update(images).set({ inPortfolio: !image.inPortfolio, updatedAt: /* @__PURE__ */ new Date() }).where(eq(images.id, id)).returning();
        return updatedImage;
      }
      async addImageCategory(imageId, categoryId) {
        await db.insert(imageCategories).values({ imageId, categoryId }).onConflictDoNothing({ target: [imageCategories.imageId, imageCategories.categoryId] });
      }
      async removeImageCategory(imageId, categoryId) {
        await db.delete(imageCategories).where(
          and(
            eq(imageCategories.imageId, imageId),
            eq(imageCategories.categoryId, categoryId)
          )
        );
      }
      async getImageCategories(imageId) {
        const results = await db.select({
          categoryId: imageCategories.categoryId
        }).from(imageCategories).where(eq(imageCategories.imageId, imageId));
        if (!results.length) return [];
        const categoryIds = results.map((r) => r.categoryId);
        return await db.select().from(portfolioCategories).where(inArray(portfolioCategories.id, categoryIds));
      }
      async bulkUpdatePortfolioStatus(imageIds, status) {
        for (const imageId of imageIds) {
          await db.update(images).set({ inPortfolio: status }).where(eq(images.id, imageId));
        }
      }
      async bulkAddToCategory(imageIds, categoryId) {
        for (const imageId of imageIds) {
          const existingCategory = await db.select().from(imageCategories).where(
            and(
              eq(imageCategories.imageId, imageId),
              eq(imageCategories.categoryId, categoryId)
            )
          );
          if (existingCategory.length === 0) {
            await db.insert(imageCategories).values({
              imageId,
              categoryId
            });
          }
        }
      }
      async toggleImageFavorite(imageId, userId) {
        const image = await this.getImage(imageId);
        if (!image) {
          throw new Error("Image not found");
        }
        const [existingFavorite] = await db.select().from(imageFavorites).where(
          and(
            eq(imageFavorites.imageId, imageId),
            eq(imageFavorites.userId, userId)
          )
        );
        if (existingFavorite) {
          await db.delete(imageFavorites).where(
            and(
              eq(imageFavorites.imageId, imageId),
              eq(imageFavorites.userId, userId)
            )
          );
          return {
            success: true,
            message: "Favorite removed",
            removed: true
          };
        }
        const [newFavorite] = await db.insert(imageFavorites).values({
          imageId,
          userId
        }).returning();
        return newFavorite;
      }
      // Image comments
      async addImageComment(comment) {
        const [newComment] = await db.insert(imageComments).values(comment).returning();
        return newComment;
      }
      async getImageComments(imageId) {
        return await db.select().from(imageComments).where(eq(imageComments.imageId, imageId)).orderBy(imageComments.createdAt);
      }
      // Reviews
      async createReview(review) {
        const [newReview] = await db.insert(reviews).values(review).returning();
        return newReview;
      }
      async getReviewsByJobId(jobId) {
        return await db.select().from(reviews).where(eq(reviews.jobId, jobId));
      }
      async getPublishedReviews() {
        return await db.select().from(reviews).where(eq(reviews.published, true)).orderBy(desc(reviews.createdAt));
      }
      async publishReview(id) {
        const [updatedReview] = await db.update(reviews).set({ published: true }).where(eq(reviews.id, id)).returning();
        return updatedReview;
      }
      async addReviewImage(reviewId, imageId) {
        try {
          await db.insert(reviewImages).values({
            reviewId,
            imageId
          }).onConflictDoNothing();
        } catch (error) {
          console.error("Error adding review image:", error);
          throw error;
        }
      }
      async getReviewImages(reviewId) {
        try {
          const result = await db.select({
            id: images.id,
            urlOriginal: images.urlOriginal,
            urlPreview: images.urlPreview,
            title: images.title
          }).from(reviewImages).innerJoin(images, eq(reviewImages.imageId, images.id)).where(eq(reviewImages.reviewId, reviewId));
          return result;
        } catch (error) {
          console.error("Error getting review images:", error);
          return [];
        }
      }
      // Referrals
      async createReferral(referral) {
        const [newReferral] = await db.insert(referrals).values(referral).returning();
        return newReferral;
      }
      async getReferralByCode(code) {
        const [referral] = await db.select().from(referrals).where(eq(referrals.code, code));
        return referral;
      }
      async getReferralsByOwner(ownerId) {
        const results = await db.select().from(referrals).where(eq(referrals.ownerUserId, ownerId)).orderBy(desc(referrals.createdAt));
        return results;
      }
      async redeemReferral(code, userId) {
        const [updatedReferral] = await db.update(referrals).set({
          redeemedByUserId: userId,
          redeemedAt: /* @__PURE__ */ new Date()
        }).where(
          and(
            eq(referrals.code, code),
            sql`${referrals.redeemedByUserId} IS NULL`
          )
        ).returning();
        return updatedReferral;
      }
      // Portfolio categories
      async createPortfolioCategory(category) {
        const [newCategory] = await db.insert(portfolioCategories).values(category).returning();
        return newCategory;
      }
      async getAllPortfolioCategories() {
        return await db.select().from(portfolioCategories).orderBy(portfolioCategories.name);
      }
      async getPortfolioCategory(id) {
        const [category] = await db.select().from(portfolioCategories).where(eq(portfolioCategories.id, id));
        return category;
      }
      // Contact messages
      async createContactMessage(message) {
        const [contactMessage] = await db.insert(contactMessages).values(message).returning();
        return contactMessage;
      }
      async getAllContactMessages() {
        return await db.select().from(contactMessages).orderBy(desc(contactMessages.createdAt));
      }
      async markContactMessageAsRead(id) {
        const [updatedMessage] = await db.update(contactMessages).set({ read: true }).where(eq(contactMessages.id, id)).returning();
        return updatedMessage;
      }
      // Booking operations
      async createBooking(booking) {
        const [newBooking] = await db.insert(bookings).values(booking).returning();
        return newBooking;
      }
      async getBooking(id) {
        const [booking] = await db.select().from(bookings).where(eq(bookings.id, id));
        return booking;
      }
      async getAllBookings() {
        return await db.select().from(bookings).orderBy(desc(bookings.createdAt));
      }
      async getBookingsByStatus(status) {
        return await db.select().from(bookings).where(eq(bookings.status, status)).orderBy(desc(bookings.createdAt));
      }
      async updateBookingStatus(id, status) {
        const [booking] = await db.update(bookings).set({
          status,
          updatedAt: /* @__PURE__ */ new Date()
        }).where(eq(bookings.id, id)).returning();
        return booking;
      }
      async verifyReferralCode(code) {
        const referral = await this.getReferralByCode(code);
        if (!referral) {
          return false;
        }
        return true;
      }
      // Website content operations
      async getWebsiteContent(id) {
        try {
          const [content] = await db.select().from(websiteContent).where(eq(websiteContent.id, id));
          return content;
        } catch (error) {
          console.error("Error getting website content:", error);
          return void 0;
        }
      }
      async getWebsiteContentByKey(key) {
        try {
          const [content] = await db.select().from(websiteContent).where(eq(websiteContent.key, key));
          return content;
        } catch (error) {
          console.error("Error getting website content by key:", error);
          return void 0;
        }
      }
      async getAllWebsiteContent() {
        try {
          return await db.select().from(websiteContent).orderBy(websiteContent.key);
        } catch (error) {
          console.error("Error getting all website content:", error);
          return [];
        }
      }
      async createWebsiteContent(content) {
        try {
          const [newContent] = await db.insert(websiteContent).values(content).returning();
          return newContent;
        } catch (error) {
          console.error("Error creating website content:", error);
          throw error;
        }
      }
      async updateWebsiteContent(id, data) {
        try {
          const [updatedContent] = await db.update(websiteContent).set({
            ...data,
            updatedAt: /* @__PURE__ */ new Date()
          }).where(eq(websiteContent.id, id)).returning();
          return updatedContent;
        } catch (error) {
          console.error("Error updating website content:", error);
          return void 0;
        }
      }
      async deleteWebsiteContent(id) {
        try {
          await db.delete(websiteContent).where(eq(websiteContent.id, id));
          return true;
        } catch (error) {
          console.error("Error deleting website content:", error);
          return false;
        }
      }
      // Mood Board Operations
      async createMoodBoard(moodBoard) {
        try {
          const [result] = await db.insert(moodBoards).values(moodBoard).returning();
          return result;
        } catch (error) {
          console.error("Error creating mood board:", error);
          throw error;
        }
      }
      async getMoodBoard(id) {
        try {
          const [result] = await db.select().from(moodBoards).where(eq(moodBoards.id, id));
          return result;
        } catch (error) {
          console.error("Error getting mood board:", error);
          return void 0;
        }
      }
      async getMoodBoardsByUserId(userId) {
        try {
          const results = await db.select().from(moodBoards).where(eq(moodBoards.userId, userId)).orderBy(desc(moodBoards.updatedAt));
          return results;
        } catch (error) {
          console.error("Error getting mood boards by user ID:", error);
          return [];
        }
      }
      async getMoodBoardsByJobId(jobId) {
        try {
          const results = await db.select().from(moodBoards).where(eq(moodBoards.jobId, jobId)).orderBy(desc(moodBoards.updatedAt));
          return results;
        } catch (error) {
          console.error("Error getting mood boards by job ID:", error);
          return [];
        }
      }
      async updateMoodBoard(id, data) {
        try {
          const [result] = await db.update(moodBoards).set({
            ...data,
            updatedAt: /* @__PURE__ */ new Date()
          }).where(eq(moodBoards.id, id)).returning();
          return result;
        } catch (error) {
          console.error("Error updating mood board:", error);
          return void 0;
        }
      }
      async deleteMoodBoard(id) {
        try {
          await db.delete(moodBoardItems).where(eq(moodBoardItems.moodBoardId, id));
          await db.delete(moodBoardCollaborators).where(eq(moodBoardCollaborators.moodBoardId, id));
          await db.delete(moodBoards).where(eq(moodBoards.id, id));
          return true;
        } catch (error) {
          console.error("Error deleting mood board:", error);
          return false;
        }
      }
      // Mood Board Items Operations
      async createMoodBoardItem(item) {
        try {
          const [result] = await db.insert(moodBoardItems).values(item).returning();
          return result;
        } catch (error) {
          console.error("Error creating mood board item:", error);
          throw error;
        }
      }
      async getMoodBoardItems(moodBoardId) {
        try {
          const results = await db.select().from(moodBoardItems).where(eq(moodBoardItems.moodBoardId, moodBoardId));
          return results;
        } catch (error) {
          console.error("Error getting mood board items:", error);
          return [];
        }
      }
      async updateMoodBoardItem(id, data) {
        try {
          const [result] = await db.update(moodBoardItems).set(data).where(eq(moodBoardItems.id, id)).returning();
          return result;
        } catch (error) {
          console.error("Error updating mood board item:", error);
          return void 0;
        }
      }
      async deleteMoodBoardItem(id) {
        try {
          await db.delete(moodBoardItems).where(eq(moodBoardItems.id, id));
          return true;
        } catch (error) {
          console.error("Error deleting mood board item:", error);
          return false;
        }
      }
      // Mood Board Collaborators Operations
      async addCollaborator(collaborator) {
        try {
          const [result] = await db.insert(moodBoardCollaborators).values(collaborator).returning();
          return result;
        } catch (error) {
          console.error("Error adding mood board collaborator:", error);
          throw error;
        }
      }
      async removeCollaborator(moodBoardId, userId) {
        try {
          await db.delete(moodBoardCollaborators).where(and(
            eq(moodBoardCollaborators.moodBoardId, moodBoardId),
            eq(moodBoardCollaborators.userId, userId)
          ));
          return true;
        } catch (error) {
          console.error("Error removing mood board collaborator:", error);
          return false;
        }
      }
      async updateCollaboratorRole(moodBoardId, userId, role) {
        try {
          const [result] = await db.update(moodBoardCollaborators).set({ role }).where(and(
            eq(moodBoardCollaborators.moodBoardId, moodBoardId),
            eq(moodBoardCollaborators.userId, userId)
          )).returning();
          return result;
        } catch (error) {
          console.error("Error updating mood board collaborator role:", error);
          return void 0;
        }
      }
      async getMoodBoardCollaborators(moodBoardId) {
        try {
          const results = await db.select().from(moodBoardCollaborators).where(eq(moodBoardCollaborators.moodBoardId, moodBoardId));
          return results;
        } catch (error) {
          console.error("Error getting mood board collaborators:", error);
          return [];
        }
      }
    };
    storage = new DatabaseStorage();
  }
});

// server/replitAuth.ts
import passport2 from "passport";
import session2 from "express-session";
import memoize from "memoizee";
import connectPg2 from "connect-pg-simple";
import { v4 as uuidv42 } from "uuid";
function updateUserSession(user, tokens2) {
  user.claims = tokens2.claims();
  user.access_token = tokens2.access_token;
  user.refresh_token = tokens2.refresh_token;
  user.expires_at = user.claims?.exp;
}
var Issuer, getOidcConfig, isAuthenticated2, isAdmin2;
var init_replitAuth = __esm({
  "server/replitAuth.ts"() {
    "use strict";
    init_storage();
    Issuer = {
      discover: async () => ({
        Client: class {
          constructor() {
          }
          authorizationUrl() {
            return "";
          }
          callbackParams() {
            return {};
          }
          callback() {
            return {};
          }
          refresh() {
            return {};
          }
          endSessionUrl() {
            return "";
          }
        }
      })
    };
    if (!process.env.REPLIT_DOMAINS) {
      throw new Error("Environment variable REPLIT_DOMAINS not provided");
    }
    if (!process.env.SESSION_SECRET) {
      console.warn("SESSION_SECRET not provided, generating a random one for development");
      process.env.SESSION_SECRET = uuidv42();
    }
    getOidcConfig = memoize(
      async () => {
        const { Issuer: Issuer2 } = __require("openid-client");
        return await Issuer2.discover(
          process.env.ISSUER_URL ?? "https://replit.com/oidc"
        );
      },
      { maxAge: 3600 * 1e3 }
    );
    isAuthenticated2 = async (req, res, next) => {
      const user = req.user;
      if (!req.isAuthenticated() || !user.expires_at) {
        return res.status(401).json({ message: "Unauthorized" });
      }
      const now = Math.floor(Date.now() / 1e3);
      if (now <= user.expires_at) {
        return next();
      }
      const refreshToken = user.refresh_token;
      if (!refreshToken) {
        return res.redirect("/api/login");
      }
      try {
        const issuerData = await getOidcConfig();
        const client_instance = new Issuer(issuerData).Client({
          client_id: process.env.REPL_ID || "",
          redirect_uris: [`https://${req.hostname}/api/callback`],
          response_types: ["code"]
        });
        const tokenResponse = await client_instance.refresh(refreshToken);
        updateUserSession(user, tokenResponse);
        return next();
      } catch (error) {
        return res.redirect("/api/login");
      }
    };
    isAdmin2 = async (req, res, next) => {
      if (!req.isAuthenticated()) {
        return res.status(401).json({ message: "Unauthorized" });
      }
      const user = req.user;
      const userId = user.claims?.sub;
      if (!userId) {
        return res.status(401).json({ message: "Unauthorized" });
      }
      const dbUser = await storage.getUser(userId);
      if (!dbUser || dbUser.role !== "admin") {
        return res.status(403).json({ message: "Forbidden: Admin access required" });
      }
      return next();
    };
  }
});

// server/routes/profile.ts
var profile_exports = {};
__export(profile_exports, {
  default: () => profile_default
});
import { Router as Router16 } from "express";
import { z as z7 } from "zod";
import { eq as eq8 } from "drizzle-orm";
var router20, profileUpdateSchema, profile_default;
var init_profile = __esm({
  "server/routes/profile.ts"() {
    "use strict";
    init_replitAuth();
    init_db();
    init_schema();
    router20 = Router16();
    profileUpdateSchema = z7.object({
      firstName: z7.string().min(1, "First name is required"),
      lastName: z7.string().min(1, "Last name is required"),
      street: z7.string().optional(),
      city: z7.string().optional(),
      state: z7.string().optional(),
      zip: z7.string().optional(),
      country: z7.string().optional(),
      mobile: z7.string().optional(),
      landline: z7.string().optional(),
      preferredContact: z7.enum(["email", "mobile", "whatsapp"]).optional()
    });
    router20.get("/profile", isAuthenticated2, async (req, res) => {
      try {
        const userId = req.user.claims.sub;
        const [profile] = await db.select().from(userProfiles).where(eq8(userProfiles.userId, userId));
        return res.json(profile || {});
      } catch (error) {
        console.error("Error fetching user profile:", error);
        return res.status(500).json({ message: "Failed to fetch user profile" });
      }
    });
    router20.post("/profile", isAuthenticated2, async (req, res) => {
      try {
        const userId = req.user.claims.sub;
        const validatedData = profileUpdateSchema.parse(req.body);
        const [existingProfile] = await db.select().from(userProfiles).where(eq8(userProfiles.userId, userId));
        if (existingProfile) {
          const [updatedProfile] = await db.update(userProfiles).set({
            ...validatedData,
            updatedAt: /* @__PURE__ */ new Date()
          }).where(eq8(userProfiles.userId, userId)).returning();
          return res.json(updatedProfile);
        } else {
          const [newProfile] = await db.insert(userProfiles).values({
            userId,
            ...validatedData
          }).returning();
          return res.json(newProfile);
        }
      } catch (error) {
        console.error("Error updating user profile:", error);
        if (error instanceof z7.ZodError) {
          return res.status(400).json({
            message: "Invalid profile data",
            errors: error.errors
          });
        }
        return res.status(500).json({ message: "Failed to update user profile" });
      }
    });
    profile_default = router20;
  }
});

// server/routes/content.ts
var content_exports = {};
__export(content_exports, {
  default: () => content_default
});
import express11 from "express";
var router21, content_default;
var init_content = __esm({
  "server/routes/content.ts"() {
    "use strict";
    init_storage();
    router21 = express11.Router();
    router21.get("/:key", async (req, res) => {
      try {
        const { key } = req.params;
        if (!key) {
          return res.status(400).json({ message: "Content key is required" });
        }
        const content = await storage.getWebsiteContentByKey(key);
        if (!content) {
          return res.status(404).json({ message: "Content not found" });
        }
        return res.status(200).json(content);
      } catch (error) {
        console.error("Error getting content:", error);
        return res.status(500).json({ message: "Failed to get content" });
      }
    });
    content_default = router21;
  }
});

// server/index.ts
import express14 from "express";

// server/routes.ts
init_storage();
init_schema();
init_db();
import { createServer } from "http";
import { WebSocketServer, WebSocket } from "ws";
import { v4 as uuidv44 } from "uuid";
import bcrypt3 from "bcryptjs";
import { eq as eq9, and as and5, desc as desc3 } from "drizzle-orm";
import { ZodError } from "zod";

// server/auth.ts
init_storage();
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import session from "express-session";
import connectPg from "connect-pg-simple";
import bcrypt from "bcryptjs";
import { v4 as uuidv4 } from "uuid";
if (!process.env.SESSION_SECRET) {
  console.warn("SESSION_SECRET not provided, generating a random one for development");
  process.env.SESSION_SECRET = uuidv4();
}
function getSession() {
  const sessionTtl = 7 * 24 * 60 * 60 * 1e3;
  const pgStore = connectPg(session);
  const sessionStore = new pgStore({
    conString: process.env.DATABASE_URL,
    createTableIfMissing: true,
    ttl: sessionTtl,
    tableName: "sessions"
  });
  return session({
    secret: process.env.SESSION_SECRET,
    store: sessionStore,
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      maxAge: sessionTtl
    }
  });
}
async function setupAuth(app2) {
  app2.use(getSession());
  app2.use(passport.initialize());
  app2.use(passport.session());
  passport.use(
    new LocalStrategy(
      {
        usernameField: "email",
        passwordField: "password"
      },
      async (email, password, done) => {
        try {
          const user = await storage.getUserByEmail(email);
          if (!user) {
            return done(null, false, { message: "Incorrect email or password" });
          }
          const passwordMatch = await bcrypt.compare(password, user.password || "");
          if (!passwordMatch) {
            return done(null, false, { message: "Incorrect email or password" });
          }
          return done(null, user);
        } catch (error) {
          return done(error);
        }
      }
    )
  );
  passport.serializeUser((user, cb) => {
    cb(null, user.id);
  });
  passport.deserializeUser(async (id, cb) => {
    try {
      const user = await storage.getUser(id);
      cb(null, user || void 0);
    } catch (error) {
      cb(error);
    }
  });
  app2.post("/api/auth/login", (req, res, next) => {
    passport.authenticate("local", (err, user, info) => {
      if (err) {
        return next(err);
      }
      if (!user) {
        return res.status(401).json({ message: info.message || "Authentication failed" });
      }
      req.logIn(user, (loginErr) => {
        if (loginErr) {
          return next(loginErr);
        }
        return res.json({
          id: user.id,
          name: user.name,
          email: user.email,
          role: user.role
        });
      });
    })(req, res, next);
  });
  app2.post("/api/auth/register", async (req, res, next) => {
    try {
      const { name, email, password, locale = "en" } = req.body;
      const existingUser = await storage.getUserByEmail(email);
      if (existingUser) {
        return res.status(400).json({ message: "Email already in use" });
      }
      const hashedPassword = await bcrypt.hash(password, 10);
      const user = await storage.createUser({
        id: uuidv4(),
        name,
        email,
        password: hashedPassword,
        role: "user",
        locale
      });
      req.logIn(user, (err) => {
        if (err) {
          return next(err);
        }
        return res.status(201).json({
          id: user.id,
          name: user.name,
          email: user.email,
          role: user.role
        });
      });
    } catch (error) {
      next(error);
    }
  });
  app2.get("/api/auth/user", isAuthenticated, (req, res) => {
    const user = req.user;
    res.json({
      id: user.id,
      name: user.name,
      email: user.email,
      role: user.role,
      locale: user.locale
    });
  });
  app2.post("/api/auth/logout", (req, res, next) => {
    req.logout((err) => {
      if (err) {
        return next(err);
      }
      res.json({ message: "Logged out successfully" });
    });
  });
}
var isAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(401).json({ message: "Unauthorized" });
};
var isAdmin = async (req, res, next) => {
  if (!req.isAuthenticated()) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  const user = req.user;
  if (!user || user.role !== "admin") {
    return res.status(403).json({ message: "Forbidden: Admin access required" });
  }
  return next();
};

// server/clientAuth.ts
init_storage();
import bcrypt2 from "bcryptjs";
import crypto from "crypto";
import "express-session";
function generateCaptchaChallenge() {
  const num1 = Math.floor(Math.random() * 10) + 1;
  const num2 = Math.floor(Math.random() * 10) + 1;
  const operation = Math.random() > 0.5 ? "+" : "-";
  const challenge = `${num1} ${operation} ${num2} = ?`;
  const answer = operation === "+" ? (num1 + num2).toString() : (num1 - num2).toString();
  return { challenge, answer };
}
async function hashPassword(password) {
  const salt = await bcrypt2.genSalt(10);
  return bcrypt2.hash(password, salt);
}
async function comparePassword(password, hash) {
  return bcrypt2.compare(password, hash);
}
var isClientAuthenticated = async (req, res, next) => {
  if (!req.isAuthenticated()) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  const user = req.user;
  const userId = user.claims?.sub;
  if (!userId) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  const dbUser = await storage.getUser(userId);
  if (!dbUser) {
    return res.status(401).json({ message: "User not found" });
  }
  req.user = dbUser;
  return next();
};
function verifyHuman(req, res, next) {
  const session3 = req.session;
  if (!session3.humanVerified) {
    return res.status(403).json({ message: "Human verification required" });
  }
  next();
}
function setupClientAuth(app2) {
  app2.get("/api/auth/captcha", (req, res) => {
    const { challenge, answer } = generateCaptchaChallenge();
    req.session.captchaAnswer = answer;
    res.json({ challenge });
  });
  app2.post("/api/auth/verify-human", (req, res) => {
    const { captchaResponse } = req.body;
    const session3 = req.session;
    if (captchaResponse === session3.captchaAnswer) {
      session3.humanVerified = true;
      res.json({ success: true, message: "Human verification successful" });
    } else {
      res.status(400).json({ success: false, message: "Incorrect verification response" });
    }
  });
  app2.post("/api/auth/register", verifyHuman, async (req, res) => {
    try {
      const { email, password, name, locale = "en" } = req.body;
      const existingUser = await storage.getUserByEmail(email);
      if (existingUser) {
        return res.status(400).json({ message: "Email already in use" });
      }
      const hashedPassword = await hashPassword(password);
      const user = await storage.createUser({
        id: crypto.randomUUID(),
        email,
        password: hashedPassword,
        name,
        role: "client",
        locale
      });
      res.status(201).json({
        message: "Account created successfully",
        user: {
          id: user.id,
          email: user.email,
          name: user.name,
          role: user.role
        }
      });
    } catch (error) {
      console.error("Registration error:", error);
      res.status(500).json({ message: "Failed to create account" });
    }
  });
  app2.post("/api/auth/login", verifyHuman, async (req, res) => {
    try {
      const { email, password } = req.body;
      const user = await storage.getUserByEmail(email);
      if (!user || !user.password) {
        return res.status(400).json({ message: "Invalid credentials" });
      }
      const isMatch = await comparePassword(password, user.password);
      if (!isMatch) {
        return res.status(400).json({ message: "Invalid credentials" });
      }
      req.login({
        id: user.id,
        email: user.email,
        name: user.name,
        role: user.role
      }, (err) => {
        if (err) {
          return res.status(500).json({ message: "Failed to establish session" });
        }
        return res.json({
          message: "Login successful",
          user: {
            id: user.id,
            email: user.email,
            name: user.name,
            role: user.role
          }
        });
      });
    } catch (error) {
      console.error("Login error:", error);
      res.status(500).json({ message: "Login failed" });
    }
  });
  app2.get("/api/auth/me", isClientAuthenticated, (req, res) => {
    const user = req.user;
    res.json({
      id: user.id,
      email: user.email,
      name: user.name,
      firstName: user.firstName || user.name?.split(" ")[0] || "",
      lastName: user.lastName || user.name?.split(" ").slice(1).join(" ") || "",
      role: user.role,
      locale: user.locale,
      image: user.image
    });
  });
  app2.get("/api/auth/logout", (req, res) => {
    req.session.destroy((err) => {
      if (err) {
        console.error("Error destroying session:", err);
        return res.status(500).json({ message: "Logout failed" });
      }
      res.clearCookie("connect.sid");
      res.json({ message: "Logged out successfully" });
    });
  });
}

// server/routes.ts
import path7 from "path";
import express12 from "express";

// server/routes/images.ts
init_storage();
init_replitAuth();
import { Router } from "express";
import multer from "multer";
import path from "path";
import fs from "fs";
import { v4 as uuidv43 } from "uuid";
import sharp from "sharp";
var uploadsDir = path.join(process.cwd(), "uploads");
var originalDir = path.join(uploadsDir, "original");
var previewDir = path.join(uploadsDir, "preview");
var watermarkDir = path.join(uploadsDir, "watermark");
[uploadsDir, originalDir, previewDir, watermarkDir].forEach((dir) => {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
});
var imageStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, originalDir);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = uuidv43();
    const extension = path.extname(file.originalname).toLowerCase();
    cb(null, `${uniqueSuffix}${extension}`);
  }
});
var upload = multer({
  storage: imageStorage,
  limits: {
    fileSize: 10 * 1024 * 1024
    // 10MB limit
  },
  fileFilter: (req, file, cb) => {
    const filetypes = /jpeg|jpg|png|gif|webp/;
    const mimetype = filetypes.test(file.mimetype);
    const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
    if (mimetype && extname) {
      return cb(null, true);
    }
    cb(new Error("Only image files are allowed"));
  }
});
var router = Router();
router.get("/images/:id", async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const image = await storage.getImage(imageId);
    if (!image) {
      return res.status(404).json({ message: "Image not found" });
    }
    res.json(image);
  } catch (error) {
    console.error("Error fetching image:", error);
    res.status(500).json({ message: "Failed to fetch image" });
  }
});
router.patch("/images/:id/portfolio", isAdmin2, async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const updatedImage = await storage.toggleImagePortfolioStatus(imageId);
    if (!updatedImage) {
      return res.status(404).json({ message: "Image not found" });
    }
    res.json(updatedImage);
  } catch (error) {
    console.error("Error toggling portfolio status:", error);
    res.status(500).json({ message: "Failed to update image" });
  }
});
router.post("/images/:id/favorite", isAuthenticated2, async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const userId = req.user?.id;
    const result = await storage.toggleImageFavorite(imageId, userId);
    res.json(result);
  } catch (error) {
    console.error("Error toggling favorite status:", error);
    res.status(500).json({ message: "Failed to update favorite status" });
  }
});
router.post("/images/:id/comments", isAuthenticated2, async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const userId = req.user?.id;
    const { comment } = req.body;
    if (!comment || typeof comment !== "string") {
      return res.status(400).json({ message: "Comment text is required" });
    }
    const result = await storage.addImageComment({
      imageId,
      userId,
      comment
    });
    res.json(result);
  } catch (error) {
    console.error("Error adding comment:", error);
    res.status(500).json({ message: "Failed to add comment" });
  }
});
router.get("/images/:id/categories", isAdmin2, async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const categories = await storage.getImageCategories(imageId);
    res.json(categories);
  } catch (error) {
    console.error("Error fetching image categories:", error);
    res.status(500).json({ message: "Failed to fetch categories" });
  }
});
router.post("/images/:id/categories", isAdmin2, async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const { categoryId } = req.body;
    if (!categoryId) {
      return res.status(400).json({ message: "Category ID is required" });
    }
    await storage.addImageCategory(imageId, parseInt(categoryId));
    res.json({ success: true });
  } catch (error) {
    console.error("Error adding category:", error);
    res.status(500).json({ message: "Failed to add category" });
  }
});
router.delete("/images/:id/categories/:categoryId", isAdmin2, async (req, res) => {
  try {
    const imageId = parseInt(req.params.id);
    const categoryId = parseInt(req.params.categoryId);
    await storage.removeImageCategory(imageId, categoryId);
    res.json({ success: true });
  } catch (error) {
    console.error("Error removing category:", error);
    res.status(500).json({ message: "Failed to remove category" });
  }
});
router.post("/images/upload", isAdmin2, upload.single("file"), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ message: "No file uploaded" });
    }
    const file = req.file;
    const filename = path.basename(file.filename, path.extname(file.filename));
    const extension = path.extname(file.filename).toLowerCase();
    const previewFilename = `${filename}_preview${extension}`;
    const watermarkFilename = `${filename}_watermark${extension}`;
    await sharp(file.path).resize(800, 800, {
      fit: "inside",
      withoutEnlargement: true
    }).toFile(path.join(previewDir, previewFilename));
    await sharp(file.path).resize(1200, 1200, {
      fit: "inside",
      withoutEnlargement: true
    }).toFile(path.join(watermarkDir, watermarkFilename));
    const imageData = {
      title: req.body.title || filename,
      description: req.body.description || "",
      urlOriginal: `/uploads/original/${file.filename}`,
      urlPreview: `/uploads/preview/${previewFilename}`,
      urlWatermark: `/uploads/watermark/${watermarkFilename}`,
      jobId: req.body.jobId ? parseInt(req.body.jobId) : null,
      inPortfolio: req.body.addToPortfolio === "true"
    };
    const image = await storage.createImage(imageData);
    if (req.body.categoryId && req.body.addToPortfolio === "true") {
      await storage.addImageCategory(image.id, parseInt(req.body.categoryId));
    }
    res.status(201).json(image);
  } catch (error) {
    console.error("Error uploading image:", error);
    res.status(500).json({ message: "Failed to upload image" });
  }
});
router.patch("/admin/images/portfolio", isAdmin2, async (req, res) => {
  try {
    const { imageIds, status } = req.body;
    if (!imageIds || !Array.isArray(imageIds) || typeof status !== "boolean") {
      return res.status(400).json({ message: "Invalid request data" });
    }
    await storage.bulkUpdatePortfolioStatus(imageIds, status);
    res.json({ success: true, updatedCount: imageIds.length });
  } catch (error) {
    console.error("Error updating portfolio status:", error);
    res.status(500).json({ message: "Failed to update images" });
  }
});
router.post("/admin/images/categories", isAdmin2, async (req, res) => {
  try {
    const { imageIds, categoryId } = req.body;
    if (!imageIds || !Array.isArray(imageIds) || !categoryId) {
      return res.status(400).json({ message: "Invalid request data" });
    }
    await storage.bulkAddToCategory(imageIds, parseInt(categoryId));
    res.json({ success: true, updatedCount: imageIds.length });
  } catch (error) {
    console.error("Error adding images to category:", error);
    res.status(500).json({ message: "Failed to update images" });
  }
});
router.post("/images/batch", isAuthenticated2, async (req, res) => {
  try {
    const { imageIds } = req.body;
    const user = req.user;
    if (!Array.isArray(imageIds) || imageIds.length === 0) {
      return res.status(400).json({ message: "Image IDs are required" });
    }
    const images2 = [];
    for (const id of imageIds) {
      const image = await storage.getImage(id);
      if (image) {
        if (user && user.role !== "admin") {
          if (image.jobId) {
            const job = await storage.getJob(image.jobId);
            if (!job || job.clientId !== user.id) {
              continue;
            }
          } else if (!image.inPortfolio) {
            continue;
          }
        }
        images2.push(image);
      }
    }
    res.status(200).json(images2);
  } catch (error) {
    console.error("Error fetching batch images:", error);
    res.status(500).json({ message: "Failed to fetch images" });
  }
});
var images_default = router;

// server/routes/uploads.ts
import express from "express";
import multer2 from "multer";
import path3 from "path";
import fs3 from "fs";
import sharp2 from "sharp";

// server/backblaze.ts
import B2 from "backblaze-b2";
import fs2 from "fs";
import path2 from "path";
import { promisify } from "util";
import * as Sentry from "@sentry/node";
import { exec as execCallback } from "child_process";
var exec = promisify(execCallback);
var b2Client = null;
async function initB2Client() {
  try {
    if (!process.env.B2_APPLICATION_KEY_ID || !process.env.B2_APPLICATION_KEY) {
      console.warn("Backblaze B2 credentials not provided. Storage features will be limited.");
      return null;
    }
    const b2 = new B2({
      applicationKeyId: process.env.B2_APPLICATION_KEY_ID,
      applicationKey: process.env.B2_APPLICATION_KEY
    });
    await b2.authorize();
    console.log("Successfully authenticated with Backblaze B2");
    b2Client = b2;
    return b2;
  } catch (error) {
    console.error("Error initializing Backblaze B2 client:", error);
    if (process.env.SENTRY_DSN) {
      Sentry.captureException(error);
    }
    return null;
  }
}
function getBucketName(bucketType) {
  const prefix = process.env.B2_BUCKET_PREFIX || "carolina-goulart";
  switch (bucketType) {
    case "client-data" /* CLIENT_DATA */:
      return process.env.B2_CLIENT_DATA_BUCKET || `${prefix}-client-data`;
    case "client-media" /* CLIENT_MEDIA */:
      return process.env.B2_CLIENT_MEDIA_BUCKET || `${prefix}-client-media`;
    case "website-content" /* WEBSITE_CONTENT */:
      return process.env.B2_WEBSITE_CONTENT_BUCKET || `${prefix}-website-content`;
    case "client-data-backup" /* CLIENT_DATA_BACKUP */:
      return process.env.B2_CLIENT_DATA_BACKUP_BUCKET || `${prefix}-client-data-backup`;
    case "client-media-backup" /* CLIENT_MEDIA_BACKUP */:
      return process.env.B2_CLIENT_MEDIA_BACKUP_BUCKET || `${prefix}-client-media-backup`;
    case "website-content-backup" /* WEBSITE_CONTENT_BACKUP */:
      return process.env.B2_WEBSITE_CONTENT_BACKUP_BUCKET || `${prefix}-website-content-backup`;
    default:
      return `${prefix}-website-content`;
  }
}
function getBucketId(bucketType) {
  switch (bucketType) {
    case "client-data" /* CLIENT_DATA */:
      return process.env.B2_CLIENT_DATA_BUCKET_ID || "";
    case "client-media" /* CLIENT_MEDIA */:
      return process.env.B2_CLIENT_MEDIA_BUCKET_ID || "";
    case "website-content" /* WEBSITE_CONTENT */:
      return process.env.B2_WEBSITE_CONTENT_BUCKET_ID || "";
    case "client-data-backup" /* CLIENT_DATA_BACKUP */:
      return process.env.B2_CLIENT_DATA_BACKUP_BUCKET_ID || "";
    case "client-media-backup" /* CLIENT_MEDIA_BACKUP */:
      return process.env.B2_CLIENT_MEDIA_BACKUP_BUCKET_ID || "";
    case "website-content-backup" /* WEBSITE_CONTENT_BACKUP */:
      return process.env.B2_WEBSITE_CONTENT_BACKUP_BUCKET_ID || "";
    default:
      return process.env.B2_WEBSITE_CONTENT_BUCKET_ID || "";
  }
}
async function uploadToB2(filePath, b2Path, bucketType = "website-content" /* WEBSITE_CONTENT */, contentType = "application/octet-stream") {
  try {
    if (!b2Client) {
      await initB2Client();
      if (!b2Client) {
        throw new Error("B2 client not initialized");
      }
    }
    const bucketName = getBucketName(bucketType);
    const bucketId = getBucketId(bucketType);
    if (!bucketId) {
      console.warn(`Bucket ID not found for bucket type: ${bucketType}. Using first available bucket.`);
    }
    const { data: uploadAuthData } = await b2Client.getUploadUrl({
      bucketId: bucketId || process.env.B2_BUCKET_ID || ""
    });
    const fileData = fs2.readFileSync(filePath);
    const fileName = path2.basename(filePath);
    const { data: fileInfo } = await b2Client.uploadFile({
      uploadUrl: uploadAuthData.uploadUrl,
      uploadAuthToken: uploadAuthData.authorizationToken,
      fileName: b2Path,
      data: fileData,
      contentType
    });
    console.log(`Successfully uploaded ${fileName} to B2 bucket ${bucketName} as ${b2Path}`);
    const downloadUrl = process.env.B2_DOWNLOAD_URL || `https://f002.backblazeb2.com/file/${bucketName}`;
    return `${downloadUrl}/${b2Path}`;
  } catch (error) {
    console.error("Error uploading file to B2:", error);
    if (process.env.SENTRY_DSN) {
      Sentry.captureException(error);
    }
    return null;
  }
}
async function backupFile(originalBucketType, filePath) {
  try {
    let backupBucketType;
    switch (originalBucketType) {
      case "client-data" /* CLIENT_DATA */:
        backupBucketType = "client-data-backup" /* CLIENT_DATA_BACKUP */;
        break;
      case "client-media" /* CLIENT_MEDIA */:
        backupBucketType = "client-media-backup" /* CLIENT_MEDIA_BACKUP */;
        break;
      case "website-content" /* WEBSITE_CONTENT */:
        backupBucketType = "website-content-backup" /* WEBSITE_CONTENT_BACKUP */;
        break;
      default:
        return true;
    }
    const originalFileData = fs2.readFileSync(filePath);
    const fileName = path2.basename(filePath);
    const contentType = getContentTypeFromFileName(fileName);
    const backupResult = await uploadToB2(
      filePath,
      fileName,
      backupBucketType,
      contentType
    );
    return !!backupResult;
  } catch (error) {
    console.error("Error backing up file:", error);
    if (process.env.SENTRY_DSN) {
      Sentry.captureException(error);
    }
    return false;
  }
}
function getContentTypeFromFileName(fileName) {
  const extension = path2.extname(fileName).toLowerCase();
  switch (extension) {
    case ".jpg":
    case ".jpeg":
      return "image/jpeg";
    case ".png":
      return "image/png";
    case ".gif":
      return "image/gif";
    case ".webp":
      return "image/webp";
    case ".svg":
      return "image/svg+xml";
    case ".pdf":
      return "application/pdf";
    case ".doc":
    case ".docx":
      return "application/msword";
    case ".xls":
    case ".xlsx":
      return "application/vnd.ms-excel";
    case ".json":
      return "application/json";
    case ".txt":
      return "text/plain";
    case ".mp4":
    case ".mov":
      return "video/mp4";
    default:
      return "application/octet-stream";
  }
}
async function backupDatabase() {
  try {
    const timestamp3 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
    const backupFilename = `backup-${timestamp3}.sql`;
    const backupPath = path2.join(__dirname, "..", "temp", backupFilename);
    if (!fs2.existsSync(path2.join(__dirname, "..", "temp"))) {
      fs2.mkdirSync(path2.join(__dirname, "..", "temp"), { recursive: true });
    }
    console.log("Creating database backup...");
    await exec(`pg_dump -U ${process.env.PGUSER} -F c -b -v -f "${backupPath}" ${process.env.PGDATABASE}`);
    console.log("Uploading database backup to client data bucket...");
    const b2Path = `backups/database/${backupFilename}`;
    const uploadResult = await uploadToB2(
      backupPath,
      b2Path,
      "client-data" /* CLIENT_DATA */,
      "application/octet-stream"
    );
    console.log("Creating backup copy in backup bucket...");
    await backupFile("client-data" /* CLIENT_DATA */, backupPath);
    fs2.unlinkSync(backupPath);
    if (uploadResult) {
      console.log(`Database backup successful: ${uploadResult}`);
      return true;
    } else {
      console.error("Database backup failed: Upload to B2 unsuccessful");
      return false;
    }
  } catch (error) {
    console.error("Database backup failed:", error);
    if (process.env.SENTRY_DSN) {
      Sentry.captureException(error);
    }
    return false;
  }
}
function scheduleBackups() {
  const scheduleNextBackup = () => {
    const now = /* @__PURE__ */ new Date();
    const nextBackup = new Date(
      now.getFullYear(),
      now.getMonth(),
      now.getDate() + 1,
      // Tomorrow
      3,
      0,
      0
      // 3:00 AM
    );
    const timeUntilBackup = nextBackup.getTime() - now.getTime();
    console.log(`Next database backup scheduled for ${nextBackup.toLocaleString()}`);
    setTimeout(async () => {
      await backupDatabase();
      scheduleNextBackup();
    }, timeUntilBackup);
  };
  scheduleNextBackup();
}
async function uploadImage(fileDataOrPath, imageName, bucketType = "client-media" /* CLIENT_MEDIA */, metadata = {}) {
  try {
    let fileData;
    let extension;
    let contentType = "image/jpeg";
    let isBuffer = false;
    let filePath = "";
    if (Buffer.isBuffer(fileDataOrPath)) {
      fileData = fileDataOrPath;
      isBuffer = true;
      extension = path2.extname(imageName).toLowerCase();
      if (!extension) extension = ".jpg";
    } else {
      filePath = fileDataOrPath;
      extension = path2.extname(filePath).toLowerCase();
      fileData = fs2.readFileSync(filePath);
    }
    if (extension === ".png") contentType = "image/png";
    else if (extension === ".gif") contentType = "image/gif";
    else if (extension === ".webp") contentType = "image/webp";
    else if (extension === ".svg") contentType = "image/svg+xml";
    const date2 = /* @__PURE__ */ new Date();
    const year = date2.getFullYear();
    const month = String(date2.getMonth() + 1).padStart(2, "0");
    if (!imageName.endsWith(extension)) {
      imageName = imageName + extension;
    }
    const b2Path = `images/${year}/${month}/${imageName}`;
    if (!b2Client) {
      await initB2Client();
      if (!b2Client) {
        throw new Error("B2 client not initialized");
      }
    }
    const bucketId = getBucketId(bucketType);
    if (!bucketId) {
      console.warn(`Bucket ID not found for bucket type: ${bucketType}. Using first available bucket.`);
    }
    const { data: uploadAuthData } = await b2Client.getUploadUrl({
      bucketId: bucketId || process.env.B2_BUCKET_ID || ""
    });
    const { data: fileInfo } = await b2Client.uploadFile({
      uploadUrl: uploadAuthData.uploadUrl,
      uploadAuthToken: uploadAuthData.authorizationToken,
      fileName: b2Path,
      data: fileData,
      contentType
    });
    const bucketName = getBucketName(bucketType);
    const downloadUrl = process.env.B2_DOWNLOAD_URL || `https://f002.backblazeb2.com/file/${bucketName}`;
    const fileUrl = `${downloadUrl}/${b2Path}`;
    if (bucketType === "client-media" /* CLIENT_MEDIA */ && metadata && metadata.inPortfolio === "true") {
      console.log("Image marked for portfolio, copying to website content bucket...");
      const portfolioPath = `portfolio/${year}/${month}/${imageName}`;
      const { data: portfolioUploadAuthData } = await b2Client.getUploadUrl({
        bucketId: getBucketId("website-content" /* WEBSITE_CONTENT */) || process.env.B2_BUCKET_ID || ""
      });
      await b2Client.uploadFile({
        uploadUrl: portfolioUploadAuthData.uploadUrl,
        uploadAuthToken: portfolioUploadAuthData.authorizationToken,
        fileName: portfolioPath,
        data: fileData,
        contentType
      });
    }
    if (!isBuffer && filePath) {
      await backupFile(bucketType, filePath);
    }
    return fileUrl;
  } catch (error) {
    console.error("Error uploading image:", error);
    if (process.env.SENTRY_DSN) {
      Sentry.captureException(error);
    }
    return null;
  }
}
async function addToPortfolio(imageId, originalUrl) {
  try {
    const tempDir = path2.join(__dirname, "..", "temp");
    if (!fs2.existsSync(tempDir)) {
      fs2.mkdirSync(tempDir, { recursive: true });
    }
    const urlParts = originalUrl.split("/");
    const fileName = urlParts[urlParts.length - 1];
    const tempFilePath = path2.join(tempDir, fileName);
    const response = await fetch(originalUrl);
    const buffer = await response.arrayBuffer();
    fs2.writeFileSync(tempFilePath, Buffer.from(buffer));
    const date2 = /* @__PURE__ */ new Date();
    const year = date2.getFullYear();
    const month = String(date2.getMonth() + 1).padStart(2, "0");
    const portfolioPath = `portfolio/${year}/${month}/${fileName}`;
    const contentType = getContentTypeFromFileName(fileName);
    const portfolioUrl = await uploadToB2(tempFilePath, portfolioPath, "website-content" /* WEBSITE_CONTENT */, contentType);
    fs2.unlinkSync(tempFilePath);
    await backupFile("website-content" /* WEBSITE_CONTENT */, tempFilePath);
    return !!portfolioUrl;
  } catch (error) {
    console.error("Error adding image to portfolio:", error);
    if (process.env.SENTRY_DSN) {
      Sentry.captureException(error);
    }
    return false;
  }
}

// server/routes/uploads.ts
init_storage();
var router2 = express.Router();
var imageStorage2 = multer2.diskStorage({
  destination: function(_req, _file, cb) {
    const dir = path3.join(__dirname, "../../uploads/temp");
    if (!fs3.existsSync(dir)) {
      fs3.mkdirSync(dir, { recursive: true });
    }
    cb(null, dir);
  },
  filename: function(_req, file, cb) {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    const ext = path3.extname(file.originalname);
    cb(null, "image-" + uniqueSuffix + ext);
  }
});
var upload2 = multer2({
  storage: imageStorage2,
  limits: {
    fileSize: 10 * 1024 * 1024
    // 10MB size limit
  },
  fileFilter: (_req, file, cb) => {
    const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error("Invalid file type. Only JPG, PNG, GIF, and WebP images are allowed."));
    }
  }
});
async function processImage(filePath, jobId, bucketType = "client-media" /* CLIENT_MEDIA */) {
  try {
    const uniqueId = path3.basename(filePath, path3.extname(filePath));
    const ext = path3.extname(filePath);
    const timestamp3 = Date.now();
    const originalName = `${uniqueId}-original${ext}`;
    const originalBuffer = fs3.readFileSync(filePath);
    const previewName = `${uniqueId}-preview${ext}`;
    const previewBuffer = await sharp2(originalBuffer).resize(1200, 1200, { fit: "inside", withoutEnlargement: true }).toBuffer();
    const watermarkName = `${uniqueId}-watermark${ext}`;
    const watermarkAssetPath = path3.join(__dirname, "../../assets/watermark.png");
    let watermarkBuffer;
    try {
      if (fs3.existsSync(watermarkAssetPath)) {
        watermarkBuffer = fs3.readFileSync(watermarkAssetPath);
      } else {
        watermarkBuffer = await sharp2({
          create: {
            width: 400,
            height: 100,
            channels: 4,
            background: { r: 255, g: 255, b: 255, alpha: 0.5 }
          }
        }).composite([{
          input: Buffer.from(
            `<svg width="400" height="100">
              <text x="50%" y="50%" font-family="sans-serif" font-size="24" 
                    fill="black" text-anchor="middle" dominant-baseline="middle">
                Carolina Goulart Photography
              </text>
            </svg>`
          )
        }]).png().toBuffer();
      }
      const watermarkedBuffer = await sharp2(originalBuffer).resize(1200, 1200, { fit: "inside", withoutEnlargement: true }).composite([{
        input: watermarkBuffer,
        gravity: "southeast",
        blend: "over"
      }]).toBuffer();
      const tempDir = path3.join(__dirname, "../../uploads/temp");
      const previewPath = path3.join(tempDir, previewName);
      const watermarkPathTemp = path3.join(tempDir, watermarkName);
      fs3.writeFileSync(previewPath, previewBuffer);
      fs3.writeFileSync(watermarkPathTemp, watermarkedBuffer);
      const originalUrl = await uploadImage(
        filePath,
        originalName,
        bucketType,
        {
          jobId: jobId.toString(),
          type: "original",
          timestamp: timestamp3.toString()
        }
      );
      const previewUrl = await uploadImage(
        previewPath,
        previewName,
        bucketType,
        {
          jobId: jobId.toString(),
          type: "preview",
          timestamp: timestamp3.toString()
        }
      );
      const watermarkUrl = await uploadImage(
        watermarkPathTemp,
        watermarkName,
        bucketType,
        {
          jobId: jobId.toString(),
          type: "watermark",
          timestamp: timestamp3.toString()
        }
      );
      fs3.unlinkSync(previewPath);
      fs3.unlinkSync(watermarkPathTemp);
      return {
        original: originalUrl || "",
        preview: previewUrl || "",
        watermark: watermarkUrl || ""
      };
    } catch (error) {
      console.error("Error processing watermark:", error);
      throw error;
    }
  } catch (error) {
    console.error("Error processing image:", error);
    throw error;
  }
}
router2.post("/upload", isAuthenticated, isAdmin, upload2.single("image"), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ message: "No image file provided" });
    }
    const jobId = parseInt(req.body.jobId || "0", 10);
    if (!jobId) {
      return res.status(400).json({ message: "Job ID is required" });
    }
    const job = await storage.getJob(jobId);
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    const bucketType = req.body.type === "portfolio" ? "website-content" /* WEBSITE_CONTENT */ : "client-media" /* CLIENT_MEDIA */;
    const imageUrls = await processImage(req.file.path, jobId, bucketType);
    const image = await storage.createImage({
      jobId,
      title: req.body.title || "Untitled",
      urlOriginal: imageUrls.original,
      urlPreview: imageUrls.preview,
      urlWatermark: imageUrls.watermark,
      inPortfolio: false,
      metadata: req.body.metadata ? JSON.parse(req.body.metadata) : {}
    });
    fs3.unlinkSync(req.file.path);
    if (job.status === "editingLocal") {
      await storage.updateJobStatus(jobId, "proofing");
    }
    return res.status(201).json({
      message: "Image uploaded successfully",
      image
    });
  } catch (error) {
    console.error("Error uploading image:", error);
    return res.status(500).json({ message: "Error uploading image" });
  }
});
router2.post("/upload/batch", isAuthenticated, isAdmin, upload2.array("images", 50), async (req, res) => {
  try {
    if (!req.files || !Array.isArray(req.files) || req.files.length === 0) {
      return res.status(400).json({ message: "No image files provided" });
    }
    const jobId = parseInt(req.body.jobId || "0", 10);
    if (!jobId) {
      return res.status(400).json({ message: "Job ID is required" });
    }
    const job = await storage.getJob(jobId);
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    const results = [];
    for (const file of req.files) {
      try {
        const bucketType = req.body.type === "portfolio" ? "website-content" /* WEBSITE_CONTENT */ : "client-media" /* CLIENT_MEDIA */;
        const imageUrls = await processImage(file.path, jobId, bucketType);
        const image = await storage.createImage({
          jobId,
          title: req.body.title || "Untitled",
          urlOriginal: imageUrls.original,
          urlPreview: imageUrls.preview,
          urlWatermark: imageUrls.watermark,
          inPortfolio: false,
          metadata: req.body.metadata ? JSON.parse(req.body.metadata) : {}
        });
        fs3.unlinkSync(file.path);
        results.push({
          filename: file.originalname,
          status: "success",
          image
        });
      } catch (error) {
        console.error(`Error processing image ${file.originalname}:`, error);
        results.push({
          filename: file.originalname,
          status: "error",
          message: "Failed to process image"
        });
        try {
          fs3.unlinkSync(file.path);
        } catch (e) {
        }
      }
    }
    if (job.status === "editingLocal" && results.some((r) => r.status === "success")) {
      await storage.updateJobStatus(jobId, "proofing");
    }
    return res.status(200).json({
      message: "Batch upload complete",
      results
    });
  } catch (error) {
    console.error("Error in batch upload:", error);
    return res.status(500).json({ message: "Error uploading images" });
  }
});
var uploads_default = router2;

// server/routes/client-data.ts
import express2 from "express";
import multer3 from "multer";
import path4 from "path";
import fs4 from "fs";
init_storage();
var router3 = express2.Router();
var docStorage = multer3.diskStorage({
  destination: function(_req, _file, cb) {
    const dir = path4.join(__dirname, "../../uploads/temp");
    if (!fs4.existsSync(dir)) {
      fs4.mkdirSync(dir, { recursive: true });
    }
    cb(null, dir);
  },
  filename: function(_req, file, cb) {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    const ext = path4.extname(file.originalname);
    cb(null, "doc-" + uniqueSuffix + ext);
  }
});
var upload3 = multer3({
  storage: docStorage,
  limits: {
    fileSize: 20 * 1024 * 1024
    // 20MB size limit for documents
  },
  fileFilter: (_req, file, cb) => {
    const allowedTypes = [
      "application/pdf",
      "application/msword",
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "application/vnd.ms-excel",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "text/plain",
      "application/json"
    ];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error("Invalid file type. Only PDF, Word, Excel, and text documents are allowed."));
    }
  }
});
router3.post("/documents/upload", isAuthenticated, upload3.single("document"), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ message: "No document file provided" });
    }
    const clientId = req.user.id;
    const jobId = parseInt(req.body.jobId || "0", 10);
    const documentType = req.body.documentType || "contract";
    if (jobId) {
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      if (job.clientId !== clientId && req.user.role !== "admin") {
        return res.status(403).json({ message: "Unauthorized access to job" });
      }
    }
    const fileName = req.file.originalname || path4.basename(req.file.path);
    const timestamp3 = Date.now();
    const clientFolder = clientId.substring(0, 8);
    let b2Path;
    if (jobId) {
      b2Path = `documents/${clientFolder}/${jobId}/${documentType}/${timestamp3}-${fileName}`;
    } else {
      b2Path = `documents/${clientFolder}/general/${documentType}/${timestamp3}-${fileName}`;
    }
    const documentUrl = await uploadToB2(
      req.file.path,
      b2Path,
      "client-data" /* CLIENT_DATA */,
      req.file.mimetype
    );
    fs4.unlinkSync(req.file.path);
    return res.status(201).json({
      message: "Document uploaded successfully",
      documentUrl,
      documentType,
      jobId: jobId || null
    });
  } catch (error) {
    console.error("Error uploading document:", error);
    return res.status(500).json({ message: "Error uploading document" });
  }
});
router3.post("/questionnaire/:jobId", isAuthenticated, async (req, res) => {
  try {
    const jobId = parseInt(req.params.jobId, 10);
    const clientId = req.user.id;
    const responses = req.body.responses;
    if (!responses || !Array.isArray(responses)) {
      return res.status(400).json({ message: "Invalid questionnaire responses" });
    }
    const job = await storage.getJob(jobId);
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    if (job.clientId !== clientId && req.user.role !== "admin") {
      return res.status(403).json({ message: "Unauthorized access to job" });
    }
    const tempDir = path4.join(__dirname, "../../uploads/temp");
    if (!fs4.existsSync(tempDir)) {
      fs4.mkdirSync(tempDir, { recursive: true });
    }
    const timestamp3 = Date.now();
    const tempFilePath = path4.join(tempDir, `questionnaire-${jobId}-${timestamp3}.json`);
    fs4.writeFileSync(tempFilePath, JSON.stringify(responses, null, 2));
    const clientFolder = clientId.substring(0, 8);
    const b2Path = `questionnaires/${clientFolder}/${jobId}/${timestamp3}-responses.json`;
    const questionnaireUrl = await uploadToB2(
      tempFilePath,
      b2Path,
      "client-data" /* CLIENT_DATA */,
      "application/json"
    );
    fs4.unlinkSync(tempFilePath);
    return res.status(200).json({
      message: "Questionnaire responses saved successfully",
      jobId
    });
  } catch (error) {
    console.error("Error saving questionnaire responses:", error);
    return res.status(500).json({ message: "Error saving questionnaire responses" });
  }
});
router3.post("/preferences", isAuthenticated, async (req, res) => {
  try {
    const clientId = req.user.id;
    const preferences = req.body.preferences;
    if (!preferences || typeof preferences !== "object") {
      return res.status(400).json({ message: "Invalid preferences data" });
    }
    const tempDir = path4.join(__dirname, "../../uploads/temp");
    if (!fs4.existsSync(tempDir)) {
      fs4.mkdirSync(tempDir, { recursive: true });
    }
    const timestamp3 = Date.now();
    const tempFilePath = path4.join(tempDir, `preferences-${clientId}-${timestamp3}.json`);
    fs4.writeFileSync(tempFilePath, JSON.stringify(preferences, null, 2));
    const clientFolder = clientId.substring(0, 8);
    const b2Path = `preferences/${clientFolder}/${timestamp3}-preferences.json`;
    const preferencesUrl = await uploadToB2(
      tempFilePath,
      b2Path,
      "client-data" /* CLIENT_DATA */,
      "application/json"
    );
    fs4.unlinkSync(tempFilePath);
    return res.status(200).json({
      message: "Client preferences saved successfully"
    });
  } catch (error) {
    console.error("Error saving client preferences:", error);
    return res.status(500).json({ message: "Error saving client preferences" });
  }
});
var client_data_default = router3;

// server/routes/checkout.ts
import { Router as Router4 } from "express";
import Stripe from "stripe";
init_storage();
var stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: "2023-10-16"
});
var router4 = Router4();
router4.post("/create-payment-intent", isClientAuthenticated, async (req, res) => {
  try {
    const userId = req.session.clientId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    const cartItems2 = await storage.getCartItems(userId);
    if (!cartItems2 || cartItems2.length === 0) {
      return res.status(400).json({ error: "Cart is empty" });
    }
    const amount = cartItems2.reduce((sum, item) => sum + (item.price || 0), 0);
    const paymentIntent = await stripe.paymentIntents.create({
      amount: Math.round(amount * 100),
      // Convert to cents
      currency: "usd",
      automatic_payment_methods: {
        enabled: true
      },
      metadata: {
        userId,
        cartItemCount: cartItems2.length.toString()
      }
    });
    res.json({ clientSecret: paymentIntent.client_secret });
  } catch (error) {
    console.error("Error creating payment intent:", error);
    res.status(500).json({ error: error.message });
  }
});
router4.post("/webhook", async (req, res) => {
  const sig = req.headers["stripe-signature"];
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET || ""
    );
  } catch (err) {
    console.error(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  switch (event.type) {
    case "payment_intent.succeeded":
      const paymentIntent = event.data.object;
      const userId = paymentIntent.metadata.userId;
      if (userId) {
        try {
          const cartItems2 = await storage.getCartItems(userId);
          if (cartItems2 && cartItems2.length > 0) {
            const order = await storage.createOrder({
              userId,
              totalAmount: Number(paymentIntent.amount) / 100,
              status: "completed",
              paymentIntentId: paymentIntent.id,
              items: cartItems2.map((item) => ({
                imageId: item.imageId,
                productId: item.productId,
                price: item.price
              }))
            });
            await storage.clearCart(userId);
            const digitalItems = cartItems2.filter((item) => item.product?.type === "digital");
            if (digitalItems.length > 0) {
              await Promise.all(
                digitalItems.map(
                  (item) => storage.createDownload({
                    userId,
                    orderId: order.id,
                    imageId: item.imageId,
                    productId: item.productId,
                    downloadUrl: item.image?.urlOriginal,
                    expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3)
                    // 30 days
                  })
                )
              );
            }
          }
        } catch (error) {
          console.error("Error processing order:", error);
        }
      }
      break;
    default:
      console.log(`Unhandled event type ${event.type}`);
  }
  res.json({ received: true });
});
router4.get("/recent", isClientAuthenticated, async (req, res) => {
  try {
    const userId = req.session.clientId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    const order = await storage.getRecentOrder(userId);
    res.json(order);
  } catch (error) {
    console.error("Error getting recent order:", error);
    res.status(500).json({ error: error.message });
  }
});
var checkout_default = router4;

// server/routes/cart.ts
import { Router as Router5 } from "express";
import { eq as eq2, and as and2 } from "drizzle-orm";
init_db();
init_schema();
var router5 = Router5();
router5.get("/", isClientAuthenticated, async (req, res) => {
  try {
    const userId = req.session.clientId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    const items = await db.query.cartItems.findMany({
      where: eq2(cartItems.userId, userId),
      with: {
        product: true,
        image: true
      }
    });
    res.json(items);
  } catch (error) {
    console.error("Error getting cart items:", error);
    res.status(500).json({ error: error.message });
  }
});
router5.post("/add", isClientAuthenticated, async (req, res) => {
  try {
    const { imageId, productId } = req.body;
    const userId = req.session.clientId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    if (!imageId || !productId) {
      return res.status(400).json({ error: "Image ID and product ID are required" });
    }
    const productResult = await db.select().from(products).where(eq2(products.id, productId));
    if (productResult.length === 0) {
      return res.status(404).json({ error: "Product not found" });
    }
    const product = productResult[0];
    const imageResult = await db.select().from(images).where(eq2(images.id, imageId));
    if (imageResult.length === 0) {
      return res.status(404).json({ error: "Image not found" });
    }
    const [cartItem] = await db.insert(cartItems).values({
      userId,
      imageId,
      productId,
      price: product.price,
      addedAt: /* @__PURE__ */ new Date()
    }).returning();
    res.json(cartItem);
  } catch (error) {
    console.error("Error adding to cart:", error);
    res.status(500).json({ error: error.message });
  }
});
router5.delete("/:id", isClientAuthenticated, async (req, res) => {
  try {
    const itemId = parseInt(req.params.id);
    const userId = req.session.clientId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    const result = await db.delete(cartItems).where(and2(
      eq2(cartItems.id, itemId),
      eq2(cartItems.userId, userId)
    )).returning();
    if (result.length === 0) {
      return res.status(404).json({ error: "Cart item not found" });
    }
    res.json({ success: true });
  } catch (error) {
    console.error("Error removing from cart:", error);
    res.status(500).json({ error: error.message });
  }
});
router5.delete("/", isClientAuthenticated, async (req, res) => {
  try {
    const userId = req.session.clientId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    await db.delete(cartItems).where(eq2(cartItems.userId, userId));
    res.json({ success: true });
  } catch (error) {
    console.error("Error clearing cart:", error);
    res.status(500).json({ error: error.message });
  }
});
var cart_default = router5;

// server/routes/portfolio.ts
init_storage();
import express3 from "express";
var router6 = express3.Router();
router6.post("/portfolio/add/:imageId", isAdmin, async (req, res) => {
  try {
    const imageId = parseInt(req.params.imageId, 10);
    const image = await storage.getImage(imageId);
    if (!image) {
      return res.status(404).json({ message: "Image not found" });
    }
    if (image.inPortfolio) {
      return res.status(400).json({ message: "Image is already in portfolio" });
    }
    const categoryIds = req.body.categoryIds || [];
    if (!Array.isArray(categoryIds)) {
      return res.status(400).json({ message: "Invalid category IDs format" });
    }
    const success = await addToPortfolio(imageId, image.urlOriginal);
    if (!success) {
      return res.status(500).json({ message: "Failed to copy image to portfolio" });
    }
    const updatedImage = await storage.updateImage(imageId, {
      inPortfolio: true
    });
    for (const categoryId of categoryIds) {
      await storage.addImageCategory(imageId, parseInt(categoryId, 10));
    }
    return res.status(200).json({
      message: "Image added to portfolio successfully",
      image: updatedImage
    });
  } catch (error) {
    console.error("Error adding image to portfolio:", error);
    return res.status(500).json({ message: "Error adding image to portfolio" });
  }
});
router6.post("/portfolio/remove/:imageId", isAdmin, async (req, res) => {
  try {
    const imageId = parseInt(req.params.imageId, 10);
    const image = await storage.getImage(imageId);
    if (!image) {
      return res.status(404).json({ message: "Image not found" });
    }
    if (!image.inPortfolio) {
      return res.status(400).json({ message: "Image is not in portfolio" });
    }
    const updatedImage = await storage.updateImage(imageId, {
      inPortfolio: false
    });
    return res.status(200).json({
      message: "Image removed from portfolio successfully",
      image: updatedImage
    });
  } catch (error) {
    console.error("Error removing image from portfolio:", error);
    return res.status(500).json({ message: "Error removing image from portfolio" });
  }
});
router6.post("/portfolio/bulk-add", isAdmin, async (req, res) => {
  try {
    const imageIds = req.body.imageIds || [];
    const categoryId = req.body.categoryId ? parseInt(req.body.categoryId, 10) : null;
    if (!Array.isArray(imageIds) || imageIds.length === 0) {
      return res.status(400).json({ message: "No image IDs provided" });
    }
    const results = [];
    for (const imageId of imageIds) {
      try {
        const image = await storage.getImage(parseInt(imageId, 10));
        if (!image) {
          results.push({ id: imageId, success: false, message: "Image not found" });
          continue;
        }
        if (image.inPortfolio) {
          results.push({ id: imageId, success: true, message: "Already in portfolio" });
          continue;
        }
        const success = await addToPortfolio(image.id, image.urlOriginal);
        if (!success) {
          results.push({ id: imageId, success: false, message: "Failed to copy to portfolio" });
          continue;
        }
        await storage.updateImage(image.id, {
          inPortfolio: true
        });
        if (categoryId) {
          await storage.addImageCategory(image.id, categoryId);
        }
        results.push({ id: imageId, success: true, message: "Added to portfolio" });
      } catch (error) {
        console.error(`Error processing image ${imageId}:`, error);
        results.push({ id: imageId, success: false, message: "Processing error" });
      }
    }
    return res.status(200).json({
      message: "Bulk portfolio update complete",
      results
    });
  } catch (error) {
    console.error("Error in bulk portfolio update:", error);
    return res.status(500).json({ message: "Error updating portfolio" });
  }
});
router6.get("/portfolio/categories", async (req, res) => {
  try {
    const categories = await storage.getAllPortfolioCategories();
    return res.status(200).json(categories);
  } catch (error) {
    console.error("Error fetching portfolio categories:", error);
    return res.status(500).json({ message: "Error fetching portfolio categories" });
  }
});
router6.post("/portfolio/categories", isAdmin, async (req, res) => {
  try {
    const { name, description, slug } = req.body;
    if (!name || !slug) {
      return res.status(400).json({ message: "Name and slug are required" });
    }
    const newCategory = await storage.createPortfolioCategory({
      name,
      description: description || null,
      slug
      // Removing 'active' property as it's not in the type definition
    });
    return res.status(201).json(newCategory);
  } catch (error) {
    console.error("Error creating portfolio category:", error);
    return res.status(500).json({ message: "Error creating portfolio category" });
  }
});
var portfolio_default = router6;

// server/routes/client.ts
import express4 from "express";
var router7 = express4.Router();
router7.get("/jobs/client", isClientAuthenticated, async (req, res) => {
  try {
    const sampleJobs = [
      {
        id: 1,
        title: "Family Portrait Session",
        date: new Date(2025, 5, 15).toISOString(),
        status: "scheduled",
        sessionType: "Family Portrait",
        location: "Sunset Beach Park",
        contractSigned: true,
        depositPaid: true,
        balancePaid: false
      },
      {
        id: 2,
        title: "Wedding Photography",
        date: new Date(2025, 7, 22).toISOString(),
        status: "confirmed",
        sessionType: "Wedding",
        location: "Harmony Gardens",
        contractSigned: true,
        depositPaid: true,
        balancePaid: false
      }
    ];
    return res.status(200).json(sampleJobs);
  } catch (error) {
    console.error("Error fetching client jobs:", error);
    return res.status(500).json({
      message: "An error occurred while fetching client jobs"
    });
  }
});
router7.get("/client/profile", isClientAuthenticated, async (req, res) => {
  try {
    const sampleProfile = {
      id: "sample-user-id",
      email: "client@example.com",
      name: "Sample Client",
      firstName: "Sample",
      lastName: "Client",
      role: "client",
      createdAt: (/* @__PURE__ */ new Date()).toISOString()
    };
    return res.status(200).json(sampleProfile);
  } catch (error) {
    console.error("Error fetching client profile:", error);
    return res.status(500).json({
      message: "An error occurred while fetching client profile"
    });
  }
});
var client_default = router7;

// server/routes/booking.ts
init_storage();
import express5 from "express";
import { z } from "zod";
var router8 = express5.Router();
var bookingRequestSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  phone: z.string().min(7),
  sessionType: z.string().min(1),
  preferredDate: z.string().refine((val) => !isNaN(Date.parse(val)), {
    message: "Invalid date format"
  }),
  alternateDate: z.string().refine((val) => val === "" || !isNaN(Date.parse(val)), {
    message: "Invalid date format"
  }).optional(),
  location: z.string().optional(),
  message: z.string().optional(),
  referralCode: z.string().optional(),
  howHeard: z.string().optional()
});
router8.post("/", async (req, res) => {
  try {
    const validatedData = bookingRequestSchema.parse(req.body);
    const sessionTypes2 = await storage.getAllSessionTypes();
    const sessionTypeObj = sessionTypes2.find((type) => type.name === validatedData.sessionType);
    if (!sessionTypeObj) {
      return res.status(400).json({
        success: false,
        message: "Invalid session type provided"
      });
    }
    const bookingData = {
      name: validatedData.name,
      email: validatedData.email,
      phone: validatedData.phone,
      sessionTypeId: sessionTypeObj.id,
      date: new Date(validatedData.preferredDate),
      status: "pending",
      // Add status field
      notes: validatedData.message || null,
      // Map message to notes field, use null instead of undefined
      location: validatedData.location || null,
      // Add location field
      userId: void 0
      // The userId field is optional in the schema
    };
    const booking = await storage.createBooking(bookingData);
    if (validatedData.referralCode) {
      try {
        const isValid = await storage.verifyReferralCode(validatedData.referralCode);
        console.log(`Referral code validation result: ${isValid}`);
      } catch (err) {
        console.error(`Error verifying referral code: ${err.message}`);
      }
    }
    res.status(201).json({
      success: true,
      message: "Booking request received successfully",
      data: booking
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        success: false,
        message: "Invalid booking data",
        errors: error.errors
      });
    } else {
      console.error("Error creating booking:", error);
      res.status(500).json({
        success: false,
        message: "Failed to process booking request"
      });
    }
  }
});
router8.get("/admin", async (req, res) => {
  try {
    const bookings2 = await storage.getAllBookings();
    res.status(200).json(bookings2);
  } catch (error) {
    console.error("Error fetching bookings:", error);
    res.status(500).json({ message: "Failed to fetch bookings" });
  }
});
router8.patch("/admin/:id", async (req, res) => {
  try {
    const { id } = req.params;
    const { status } = req.body;
    if (!status || !["pending", "confirmed", "cancelled", "completed"].includes(status)) {
      return res.status(400).json({ message: "Invalid status" });
    }
    const booking = await storage.updateBookingStatus(Number(id), status);
    if (!booking) {
      return res.status(404).json({ message: "Booking not found" });
    }
    res.status(200).json(booking);
  } catch (error) {
    console.error("Error updating booking:", error);
    res.status(500).json({ message: "Failed to update booking" });
  }
});
var booking_default = router8;

// server/routes/admin-content.ts
init_storage();
import express6 from "express";
import { z as z2 } from "zod";
import multer4 from "multer";
import path5 from "path";
var router9 = express6.Router();
var upload4 = multer4({
  storage: multer4.memoryStorage(),
  limits: {
    fileSize: 5 * 1024 * 1024
    // 5MB
  },
  fileFilter: (req, file, cb) => {
    const filetypes = /jpeg|jpg|png|webp/;
    const mimetype = filetypes.test(file.mimetype);
    const extname = filetypes.test(path5.extname(file.originalname).toLowerCase());
    if (mimetype && extname) {
      return cb(null, true);
    }
    cb(new Error("Error: Images Only!"));
  }
});
var createContentSchema = z2.object({
  name: z2.string().min(1),
  key: z2.string().min(1),
  type: z2.enum(["text", "textarea", "image", "link"]),
  value_en: z2.string().optional().default(""),
  value_pt: z2.string().optional().default(""),
  value_es: z2.string().optional().default("")
});
var updateContentSchema = z2.object({
  value_en: z2.string().optional(),
  value_pt: z2.string().optional(),
  value_es: z2.string().optional()
});
router9.get("/", isAdmin, async (req, res) => {
  try {
    const content = await storage.getAllWebsiteContent();
    res.status(200).json(content);
  } catch (error) {
    console.error("Error getting website content:", error);
    res.status(500).json({ message: "Failed to get website content" });
  }
});
router9.post("/", isAdmin, async (req, res) => {
  try {
    const validatedData = createContentSchema.parse(req.body);
    const content = await storage.createWebsiteContent(validatedData);
    res.status(201).json(content);
  } catch (error) {
    if (error instanceof z2.ZodError) {
      res.status(400).json({ message: "Validation error", errors: error.errors });
    } else {
      console.error("Error creating website content:", error);
      res.status(500).json({ message: "Failed to create website content" });
    }
  }
});
router9.patch("/:id", isAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const contentId = parseInt(id);
    const validatedData = updateContentSchema.parse(req.body);
    const content = await storage.updateWebsiteContent(contentId, validatedData);
    if (!content) {
      return res.status(404).json({ message: "Content not found" });
    }
    res.status(200).json(content);
  } catch (error) {
    if (error instanceof z2.ZodError) {
      res.status(400).json({ message: "Validation error", errors: error.errors });
    } else {
      console.error("Error updating website content:", error);
      res.status(500).json({ message: "Failed to update website content" });
    }
  }
});
router9.delete("/:id", isAdmin, async (req, res) => {
  try {
    const { id } = req.params;
    const contentId = parseInt(id);
    const success = await storage.deleteWebsiteContent(contentId);
    if (!success) {
      return res.status(404).json({ message: "Content not found" });
    }
    res.status(200).json({ message: "Content deleted successfully" });
  } catch (error) {
    console.error("Error deleting website content:", error);
    res.status(500).json({ message: "Failed to delete website content" });
  }
});
router9.post("/:id/image", isAdmin, upload4.single("image"), async (req, res) => {
  try {
    const { id } = req.params;
    const contentId = parseInt(id);
    if (!req.file) {
      return res.status(400).json({ message: "No image file provided" });
    }
    const contentItem = await storage.getWebsiteContent(contentId);
    if (!contentItem) {
      return res.status(404).json({ message: "Content not found" });
    }
    const fileName = `website-content/${contentId}-${Date.now()}-${req.file.originalname}`;
    const imageUrl = await uploadImage(req.file.buffer, fileName, "website-content" /* WEBSITE_CONTENT */);
    const updatedContent = await storage.updateWebsiteContent(contentId, {
      [req.body.language === "pt-BR" ? "value_pt" : req.body.language === "es" ? "value_es" : "value_en"]: imageUrl
    });
    res.status(200).json(updatedContent);
  } catch (error) {
    console.error("Error uploading image for content:", error);
    res.status(500).json({ message: "Failed to upload image" });
  }
});
var admin_content_default = router9;

// server/routes/user-profile.ts
init_replitAuth();
init_db();
init_schema();
import { Router as Router10 } from "express";
import { z as z3 } from "zod";
import { eq as eq3 } from "drizzle-orm";
var router10 = Router10();
var updateProfileSchema = z3.object({
  firstName: z3.string().min(1),
  lastName: z3.string().min(1),
  street: z3.string().optional(),
  city: z3.string().optional(),
  state: z3.string().optional(),
  zip: z3.string().optional(),
  country: z3.string().optional(),
  mobile: z3.string().optional(),
  landline: z3.string().optional(),
  preferredContact: z3.enum(["email", "mobile", "whatsapp"]).default("email")
});
router10.get("/profile", isAuthenticated2, async (req, res) => {
  try {
    const userId = req.user.claims.sub;
    const [userProfile] = await db.select().from(userProfiles).where(eq3(userProfiles.userId, userId));
    if (!userProfile) {
      return res.json({});
    }
    return res.json(userProfile);
  } catch (error) {
    console.error("Error fetching user profile:", error);
    return res.status(500).json({ message: "Failed to fetch user profile" });
  }
});
router10.post("/profile", isAuthenticated2, async (req, res) => {
  try {
    const userId = req.user.claims.sub;
    const validatedData = updateProfileSchema.parse(req.body);
    const [existingProfile] = await db.select().from(userProfiles).where(eq3(userProfiles.userId, userId));
    await db.update(users).set({
      name: `${validatedData.firstName} ${validatedData.lastName}`,
      updatedAt: /* @__PURE__ */ new Date()
    }).where(eq3(users.id, userId));
    let userProfile;
    if (existingProfile) {
      const [updatedProfile] = await db.update(userProfiles).set({
        street: validatedData.street,
        city: validatedData.city,
        state: validatedData.state,
        zip: validatedData.zip,
        country: validatedData.country,
        mobile: validatedData.mobile,
        landline: validatedData.landline,
        preferredContact: validatedData.preferredContact,
        updatedAt: /* @__PURE__ */ new Date()
      }).where(eq3(userProfiles.userId, userId)).returning();
      userProfile = updatedProfile;
    } else {
      const [newProfile] = await db.insert(userProfiles).values({
        userId,
        street: validatedData.street,
        city: validatedData.city,
        state: validatedData.state,
        zip: validatedData.zip,
        country: validatedData.country,
        mobile: validatedData.mobile,
        landline: validatedData.landline,
        preferredContact: validatedData.preferredContact
      }).returning();
      userProfile = newProfile;
    }
    return res.json(userProfile);
  } catch (error) {
    console.error("Error updating user profile:", error);
    if (error instanceof z3.ZodError) {
      return res.status(400).json({
        message: "Invalid profile data",
        errors: error.errors
      });
    }
    return res.status(500).json({ message: "Failed to update user profile" });
  }
});
var user_profile_default = router10;

// server/routes/blog.ts
init_db();
init_schema();
import { Router as Router11 } from "express";
import { eq as eq5, desc as desc2, ilike } from "drizzle-orm";

// server/lib/socialMedia.ts
init_db();
init_schema();
import { eq as eq4, sql as sql2 } from "drizzle-orm";
async function shareToSocialMedia(content) {
  const results = /* @__PURE__ */ new Map();
  try {
    const accounts = await db.query.socialMediaAccounts.findMany({
      where: eq4(socialMediaAccounts.active, true)
    });
    if (!accounts || accounts.length === 0) {
      console.log("No active social media accounts found");
      return results;
    }
    for (const account of accounts) {
      try {
        let result;
        switch (account.platform) {
          case "facebook":
            result = await shareToFacebook(account, content);
            break;
          case "instagram":
            result = await shareToInstagram(account, content);
            break;
          case "linkedin":
            result = await shareToLinkedin(account, content);
            break;
          default:
            result = { success: false, message: `Unsupported platform: ${account.platform}` };
        }
        results.set(account.platform, result);
      } catch (error) {
        console.error(`Error sharing to ${account.platform}:`, error);
        results.set(account.platform, {
          success: false,
          message: `Failed to share to ${account.platform}: ${error instanceof Error ? error.message : "Unknown error"}`
        });
      }
    }
    return results;
  } catch (error) {
    console.error("Error in shareToSocialMedia:", error);
    return results;
  }
}
async function shareToFacebook(account, content) {
  if (!account.accessToken) {
    return { success: false, message: "No Facebook access token available" };
  }
  try {
    console.log(`[MOCK] Sharing to Facebook: ${content.text}`);
    await updateLastPosted(account.id);
    return {
      success: true,
      message: "Content shared to Facebook",
      postId: `fb_${Date.now()}`,
      url: "https://facebook.com/carolinagoulart.photography"
    };
  } catch (error) {
    console.error("Facebook sharing error:", error);
    throw error;
  }
}
async function shareToInstagram(account, content) {
  if (!account.accessToken) {
    return { success: false, message: "No Instagram access token available" };
  }
  try {
    console.log(`[MOCK] Sharing to Instagram: ${content.text}`);
    await updateLastPosted(account.id);
    return {
      success: true,
      message: "Content shared to Instagram",
      postId: `ig_${Date.now()}`,
      url: "https://instagram.com/carolinagoulart.photography"
    };
  } catch (error) {
    console.error("Instagram sharing error:", error);
    throw error;
  }
}
async function shareToLinkedin(account, content) {
  if (!account.accessToken) {
    return { success: false, message: "No LinkedIn access token available" };
  }
  try {
    console.log(`[MOCK] Sharing to LinkedIn: ${content.text}`);
    await updateLastPosted(account.id);
    return {
      success: true,
      message: "Content shared to LinkedIn",
      postId: `li_${Date.now()}`,
      url: "https://linkedin.com/in/carolinagoulart"
    };
  } catch (error) {
    console.error("LinkedIn sharing error:", error);
    throw error;
  }
}
async function updateLastPosted(accountId) {
  await db.update(socialMediaAccounts).set({
    lastPostAt: /* @__PURE__ */ new Date(),
    postCount: sql2`${socialMediaAccounts.postCount} + 1`
  }).where(eq4(socialMediaAccounts.id, accountId));
}

// server/routes/blog.ts
import multer5 from "multer";
import path6 from "path";
import fs5 from "fs";
var router11 = Router11();
var upload5 = multer5({ dest: "uploads/temp" });
router11.get("/", async (req, res) => {
  try {
    const { locale = "en" } = req.query;
    const posts = await db.query.blogPosts.findMany({
      where: eq5(blogPosts.published, true),
      orderBy: [desc2(blogPosts.publishedAt)]
    });
    const filteredPosts = locale ? posts.filter((post) => post.locales?.includes(locale)) : posts;
    return res.json(filteredPosts);
  } catch (error) {
    console.error("Error fetching blog posts:", error);
    return res.status(500).json({ message: "Failed to fetch blog posts" });
  }
});
router11.get("/:id", async (req, res) => {
  try {
    const postId = parseInt(req.params.id);
    const post = await db.query.blogPosts.findFirst({
      where: eq5(blogPosts.id, postId),
      with: {
        blogPostJobs: {
          with: {
            job: true
          }
        }
      }
    });
    if (!post) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    if (!post.published && (!req.user || req.user.role !== "admin")) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    return res.json(post);
  } catch (error) {
    console.error("Error fetching blog post:", error);
    return res.status(500).json({ message: "Failed to fetch blog post" });
  }
});
router11.get("/slug/:slug", async (req, res) => {
  try {
    const { slug } = req.params;
    const post = await db.query.blogPosts.findFirst({
      where: eq5(blogPosts.slug, slug),
      with: {
        blogPostJobs: {
          with: {
            job: true
          }
        }
      }
    });
    if (!post) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    if (!post.published && (!req.user || req.user.role !== "admin")) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    return res.json(post);
  } catch (error) {
    console.error("Error fetching blog post:", error);
    return res.status(500).json({ message: "Failed to fetch blog post" });
  }
});
router11.post("/", isAdmin, async (req, res) => {
  try {
    const {
      title,
      slug,
      content,
      excerpt,
      featuredImageUrl,
      published = false,
      locales = ["en"],
      jobs: jobs2 = []
    } = req.body;
    if (!title || !slug || !content) {
      return res.status(400).json({ message: "Title, slug, and content are required" });
    }
    const existingPost = await db.query.blogPosts.findFirst({
      where: eq5(blogPosts.slug, slug)
    });
    if (existingPost) {
      return res.status(400).json({ message: "A post with this slug already exists" });
    }
    const newPost = await db.insert(blogPosts).values({
      title,
      slug,
      content,
      excerpt,
      featuredImageUrl,
      published,
      publishedAt: published ? /* @__PURE__ */ new Date() : null,
      locales,
      createdAt: /* @__PURE__ */ new Date(),
      updatedAt: /* @__PURE__ */ new Date()
    }).returning();
    if (jobs2 && jobs2.length > 0) {
      for (const jobId of jobs2) {
        await db.insert(blogPostJobs).values({
          blogPostId: newPost[0].id,
          jobId
        });
      }
    }
    if (published && newPost[0].id) {
      try {
        let shareText = title;
        if (excerpt) {
          shareText += "\n\n" + excerpt;
        }
        shareText += "\n\nRead more: https://carolinagoulart.photography/blog/" + slug;
        const socialResult = await shareToSocialMedia({
          text: shareText,
          imageUrl: featuredImageUrl,
          link: "https://carolinagoulart.photography/blog/" + slug
        });
        console.log("Social media sharing results:", Object.fromEntries(socialResult));
      } catch (error) {
        console.error("Error sharing to social media:", error);
      }
    }
    return res.status(201).json(newPost[0]);
  } catch (error) {
    console.error("Error creating blog post:", error);
    return res.status(500).json({ message: "Failed to create blog post" });
  }
});
router11.patch("/:id", isAdmin, async (req, res) => {
  try {
    const postId = parseInt(req.params.id);
    const {
      title,
      slug,
      content,
      excerpt,
      featuredImageUrl,
      published,
      locales,
      jobs: jobs2
    } = req.body;
    const post = await db.query.blogPosts.findFirst({
      where: eq5(blogPosts.id, postId)
    });
    if (!post) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    if (slug && slug !== post.slug) {
      const existingPost = await db.query.blogPosts.findFirst({
        where: eq5(blogPosts.slug, slug)
      });
      if (existingPost && existingPost.id !== postId) {
        return res.status(400).json({ message: "A post with this slug already exists" });
      }
    }
    const updateData = {
      updatedAt: /* @__PURE__ */ new Date()
    };
    if (title !== void 0) updateData.title = title;
    if (slug !== void 0) updateData.slug = slug;
    if (content !== void 0) updateData.content = content;
    if (excerpt !== void 0) updateData.excerpt = excerpt;
    if (featuredImageUrl !== void 0) updateData.featuredImageUrl = featuredImageUrl;
    if (locales !== void 0) updateData.locales = locales;
    if (published !== void 0 && published !== post.published) {
      updateData.published = published;
      if (published && !post.publishedAt) {
        updateData.publishedAt = /* @__PURE__ */ new Date();
      }
    }
    const updatedPost = await db.update(blogPosts).set(updateData).where(eq5(blogPosts.id, postId)).returning();
    if (jobs2 !== void 0) {
      await db.delete(blogPostJobs).where(eq5(blogPostJobs.blogPostId, postId));
      if (jobs2.length > 0) {
        for (const jobId of jobs2) {
          await db.insert(blogPostJobs).values({
            blogPostId: postId,
            jobId
          });
        }
      }
    }
    if (published && !post.published && updatedPost[0].id) {
      try {
        let shareText = updatedPost[0].title;
        if (updatedPost[0].excerpt) {
          shareText += "\n\n" + updatedPost[0].excerpt;
        }
        shareText += "\n\nRead more: https://carolinagoulart.photography/blog/" + updatedPost[0].slug;
        const imageUrl = updatedPost[0].featuredImageUrl || void 0;
        const socialResult = await shareToSocialMedia({
          text: shareText,
          imageUrl,
          link: "https://carolinagoulart.photography/blog/" + updatedPost[0].slug
        });
        console.log("Social media sharing results:", Object.fromEntries(socialResult));
      } catch (error) {
        console.error("Error sharing to social media:", error);
      }
    }
    return res.json(updatedPost[0]);
  } catch (error) {
    console.error("Error updating blog post:", error);
    return res.status(500).json({ message: "Failed to update blog post" });
  }
});
router11.delete("/:id", isAdmin, async (req, res) => {
  try {
    const postId = parseInt(req.params.id);
    const post = await db.query.blogPosts.findFirst({
      where: eq5(blogPosts.id, postId)
    });
    if (!post) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    await db.delete(blogPostJobs).where(eq5(blogPostJobs.blogPostId, postId));
    await db.delete(blogMedia).where(eq5(blogMedia.blogPostId, postId));
    await db.delete(blogPosts).where(eq5(blogPosts.id, postId));
    return res.json({ message: "Blog post deleted successfully" });
  } catch (error) {
    console.error("Error deleting blog post:", error);
    return res.status(500).json({ message: "Failed to delete blog post" });
  }
});
router11.get("/search/:query", async (req, res) => {
  try {
    const { query } = req.params;
    const { locale = "en" } = req.query;
    const posts = await db.query.blogPosts.findMany({
      where: (fields, { and: and6, or }) => and6(
        eq5(fields.published, true),
        or(
          ilike(fields.title, `%${query}%`),
          ilike(fields.excerpt, `%${query}%`)
        )
      ),
      orderBy: [desc2(blogPosts.publishedAt)]
    });
    const filteredPosts = locale ? posts.filter((post) => post.locales?.includes(locale)) : posts;
    return res.json(filteredPosts);
  } catch (error) {
    console.error("Error searching blog posts:", error);
    return res.status(500).json({ message: "Failed to search blog posts" });
  }
});
router11.post("/media", isAdmin, upload5.single("file"), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ message: "No file uploaded" });
    }
    const { blogPostId } = req.body;
    const file = req.file;
    if (blogPostId) {
      const post = await db.query.blogPosts.findFirst({
        where: eq5(blogPosts.id, parseInt(blogPostId))
      });
      if (!post) {
        fs5.unlinkSync(file.path);
        return res.status(404).json({ message: "Blog post not found" });
      }
    }
    const fileExt = path6.extname(file.originalname).toLowerCase();
    const isImage = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"].includes(fileExt);
    const isVideo = [".mp4", ".mov", ".avi", ".webm"].includes(fileExt);
    if (!isImage && !isVideo) {
      fs5.unlinkSync(file.path);
      return res.status(400).json({ message: "Unsupported file type" });
    }
    const now = /* @__PURE__ */ new Date();
    const fileName = `blog/${now.getFullYear()}/${now.getMonth() + 1}/${path6.basename(file.originalname, fileExt)}-${Date.now()}${fileExt}`;
    const uploadResult = await uploadImage(
      file.path,
      // Pass the file path as a string
      fileName,
      "website-content" /* WEBSITE_CONTENT */,
      { type: "blog-media", timestamp: Date.now().toString() }
    );
    fs5.unlinkSync(file.path);
    if (!uploadResult) {
      return res.status(500).json({ message: "Failed to upload file" });
    }
    if (blogPostId && uploadResult) {
      await db.insert(blogMedia).values({
        url: uploadResult,
        // We've already asserted uploadResult as string
        type: isImage ? "image" : "video",
        blogPostId: parseInt(blogPostId)
        // createdAt and updatedAt have default values in the schema
      });
    }
    if (!uploadResult) {
      return res.status(500).json({ message: "Failed to upload media" });
    }
    return res.json({
      url: uploadResult,
      // uploadImage returns the URL directly as a string
      fileName,
      type: isImage ? "image" : "video"
    });
  } catch (error) {
    console.error("Error uploading blog media:", error);
    if (req.file) {
      try {
        fs5.unlinkSync(req.file.path);
      } catch (unlinkError) {
        console.error("Error cleaning up temp file:", unlinkError);
      }
    }
    return res.status(500).json({ message: "Failed to upload media" });
  }
});
router11.post("/:id/share", isAdmin, async (req, res) => {
  try {
    const postId = parseInt(req.params.id);
    const { platforms } = req.body;
    const post = await db.query.blogPosts.findFirst({
      where: eq5(blogPosts.id, postId)
    });
    if (!post) {
      return res.status(404).json({ message: "Blog post not found" });
    }
    if (!post.published) {
      return res.status(400).json({ message: "Cannot share unpublished post" });
    }
    let shareText = post.title;
    if (post.excerpt) {
      shareText += "\n\n" + post.excerpt;
    }
    shareText += "\n\nRead more: https://carolinagoulart.photography/blog/" + post.slug;
    const imageUrl = post.featuredImageUrl === null ? void 0 : post.featuredImageUrl;
    const socialResult = await shareToSocialMedia({
      text: shareText,
      imageUrl,
      link: "https://carolinagoulart.photography/blog/" + post.slug
    });
    let filteredResults = socialResult;
    if (platforms && Array.isArray(platforms) && platforms.length > 0) {
      filteredResults = /* @__PURE__ */ new Map();
      for (const platform of platforms) {
        if (socialResult.has(platform)) {
          filteredResults.set(platform, socialResult.get(platform));
        }
      }
    }
    return res.json({
      message: "Post shared to social media",
      results: Object.fromEntries(filteredResults)
    });
  } catch (error) {
    console.error("Error sharing post to social media:", error);
    return res.status(500).json({ message: "Failed to share post to social media" });
  }
});
var blog_default = router11;

// server/routes/job-members.ts
init_db();
init_schema();
import { Router as Router12 } from "express";
import { eq as eq6, and as and3 } from "drizzle-orm";

// server/email.ts
async function sendEmail(options) {
  console.log("Email would be sent:");
  console.log("To:", options.to);
  console.log("Subject:", options.subject);
  console.log("Text:", options.text);
  return true;
}

// server/routes/job-members.ts
var router12 = Router12();
router12.get("/:jobId/members", isAuthenticated, async (req, res) => {
  try {
    const jobId = parseInt(req.params.jobId);
    const job = await db.query.jobs.findFirst({
      where: eq6(jobs.id, jobId)
    });
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    const user = req.user;
    const isUserMember = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, user.id)
      )
    });
    if (!isUserMember && user.role !== "admin" && job.clientId !== user.id) {
      return res.status(403).json({ message: "Not authorized to view members for this job" });
    }
    const members = await db.query.jobMembers.findMany({
      where: eq6(jobMembers.jobId, jobId),
      with: {
        user: true
      }
    });
    const formattedMembers = members.map((member) => ({
      userId: member.user.id,
      email: member.user.email,
      name: member.user.name,
      role: member.role,
      profileImageUrl: member.user.image
    }));
    return res.json(formattedMembers);
  } catch (error) {
    console.error("Error fetching job members:", error);
    return res.status(500).json({ message: "Failed to fetch job members" });
  }
});
router12.post("/:jobId/members", isAuthenticated, async (req, res) => {
  try {
    const jobId = parseInt(req.params.jobId);
    const { email } = req.body;
    if (!email) {
      return res.status(400).json({ message: "Email is required" });
    }
    const job = await db.query.jobs.findFirst({
      where: eq6(jobs.id, jobId)
    });
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    const currentUser = req.user;
    const isJobOwner = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, currentUser.id),
        eq6(jobMembers.role, "owner")
      )
    });
    if (!isJobOwner && currentUser.role !== "admin" && job.clientId !== currentUser.id) {
      return res.status(403).json({ message: "Not authorized to add members to this job" });
    }
    let targetUser = await db.query.users.findFirst({
      where: eq6(users.email, email)
    });
    if (!targetUser) {
      const pendingUserId = `pending-${Date.now()}-${Math.random().toString(36).substring(2)}`;
      const result = await db.insert(users).values({
        id: pendingUserId,
        email,
        role: "user",
        createdAt: /* @__PURE__ */ new Date(),
        updatedAt: /* @__PURE__ */ new Date()
      }).returning();
      targetUser = result[0];
      try {
        await sendEmail({
          to: email,
          subject: `You've been invited to view photographs from Carolina Goulart Photography`,
          text: `You've been invited to view photographs from a photoshoot at Carolina Goulart Photography. Sign up to view your images: https://carolinagoulart.photography/register?invited=true&email=${encodeURIComponent(email)}&jobId=${jobId}`,
          html: `
            <h1>You've been invited to view photographs from Carolina Goulart Photography</h1>
            <p>You've been invited to view photographs from a photoshoot.</p>
            <p><a href="https://carolinagoulart.photography/register?invited=true&email=${encodeURIComponent(email)}&jobId=${jobId}">Click here to create an account and view your images</a></p>
          `
        });
      } catch (error) {
        console.error("Error sending invitation email:", error);
      }
    }
    const existingMember = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, targetUser.id)
      )
    });
    if (existingMember) {
      return res.status(400).json({ message: "User is already a member of this job" });
    }
    await db.insert(jobMembers).values({
      jobId,
      userId: targetUser.id,
      role: "member"
      // Default role is member
    });
    if (!targetUser.id.startsWith("pending-")) {
      try {
        await sendEmail({
          to: email,
          subject: `You've been granted access to photos from Carolina Goulart Photography`,
          text: `You've been granted access to photos from a recent session at Carolina Goulart Photography. Sign in to view your images: https://carolinagoulart.photography/login`,
          html: `
            <h1>You've been granted access to photos</h1>
            <p>You've been given access to photos from a recent session at Carolina Goulart Photography.</p>
            <p><a href="https://carolinagoulart.photography/login">Sign in to view your images</a></p>
          `
        });
      } catch (error) {
        console.error("Error sending notification email:", error);
      }
    }
    return res.status(201).json({
      message: "Member added successfully",
      userId: targetUser.id,
      email: targetUser.email,
      pending: targetUser.id.startsWith("pending-")
    });
  } catch (error) {
    console.error("Error adding job member:", error);
    return res.status(500).json({ message: "Failed to add member to job" });
  }
});
router12.delete("/:jobId/members/:userId", isAuthenticated, async (req, res) => {
  try {
    const jobId = parseInt(req.params.jobId);
    const { userId } = req.params;
    const job = await db.query.jobs.findFirst({
      where: eq6(jobs.id, jobId)
    });
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    const currentUser = req.user;
    const isJobOwner = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, currentUser.id),
        eq6(jobMembers.role, "owner")
      )
    });
    if (!isJobOwner && currentUser.role !== "admin" && job.clientId !== currentUser.id) {
      return res.status(403).json({ message: "Not authorized to remove members from this job" });
    }
    const member = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, userId)
      )
    });
    if (!member) {
      return res.status(404).json({ message: "Member not found" });
    }
    if (member.role === "owner") {
      return res.status(400).json({ message: "Cannot remove the owner of the job" });
    }
    await db.delete(jobMembers).where(and3(
      eq6(jobMembers.jobId, jobId),
      eq6(jobMembers.userId, userId)
    ));
    try {
      const user = await db.query.users.findFirst({
        where: eq6(users.id, userId)
      });
      if (user && user.email) {
        await sendEmail({
          to: user.email,
          subject: `Access to photos has been revoked`,
          text: `Your access to photos from a session at Carolina Goulart Photography has been revoked. If you believe this is an error, please contact the photographer.`,
          html: `
            <h1>Access to photos has been revoked</h1>
            <p>Your access to photos from a session at Carolina Goulart Photography has been revoked.</p>
            <p>If you believe this is an error, please contact the photographer.</p>
          `
        });
      }
    } catch (error) {
      console.error("Error sending notification email:", error);
    }
    return res.json({ message: "Member removed successfully" });
  } catch (error) {
    console.error("Error removing job member:", error);
    return res.status(500).json({ message: "Failed to remove member from job" });
  }
});
router12.patch("/:jobId/members/:userId/role", isAuthenticated, async (req, res) => {
  try {
    const jobId = parseInt(req.params.jobId);
    const { userId } = req.params;
    const { role } = req.body;
    if (!role || !["owner", "member"].includes(role)) {
      return res.status(400).json({ message: "Valid role is required (owner or member)" });
    }
    const job = await db.query.jobs.findFirst({
      where: eq6(jobs.id, jobId)
    });
    if (!job) {
      return res.status(404).json({ message: "Job not found" });
    }
    const currentUser = req.user;
    const isJobOwner = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, currentUser.id),
        eq6(jobMembers.role, "owner")
      )
    });
    if (!isJobOwner && currentUser.role !== "admin" && job.clientId !== currentUser.id) {
      return res.status(403).json({ message: "Not authorized to change roles for this job" });
    }
    const member = await db.query.jobMembers.findFirst({
      where: and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.userId, userId)
      )
    });
    if (!member) {
      return res.status(404).json({ message: "Member not found" });
    }
    if (role === "owner") {
      await db.update(jobMembers).set({ role: "member" }).where(and3(
        eq6(jobMembers.jobId, jobId),
        eq6(jobMembers.role, "owner")
      ));
    }
    await db.update(jobMembers).set({ role }).where(and3(
      eq6(jobMembers.jobId, jobId),
      eq6(jobMembers.userId, userId)
    ));
    return res.json({ message: "Member role updated successfully" });
  } catch (error) {
    console.error("Error updating job member role:", error);
    return res.status(500).json({ message: "Failed to update member role" });
  }
});
var job_members_default = router12;

// server/routes/reviews.ts
init_storage();
import { Router as Router13 } from "express";
import multer6 from "multer";
import { z as z4 } from "zod";
var router13 = Router13();
var upload6 = multer6({
  storage: multer6.memoryStorage(),
  limits: { fileSize: 5 * 1024 * 1024 }
  // 5MB limit
});
var reviewSchema = z4.object({
  jobId: z4.number().int().positive(),
  name: z4.string().min(2, { message: "Name must be at least 2 characters" }),
  email: z4.string().email({ message: "Please enter a valid email address" }),
  stars: z4.number().int().min(1).max(5),
  text: z4.string().min(10, { message: "Please provide more details about your experience" })
});
router13.get("/", async (_req, res) => {
  try {
    const reviews2 = await storage.getPublishedReviews();
    return res.json(reviews2);
  } catch (error) {
    console.error("Error fetching published reviews:", error);
    return res.status(500).json({ message: "Failed to fetch reviews" });
  }
});
router13.post("/", verifyHuman, async (req, res) => {
  try {
    const validationResult = reviewSchema.safeParse(req.body);
    if (!validationResult.success) {
      return res.status(400).json({
        message: "Invalid review data",
        errors: validationResult.error.errors
      });
    }
    const reviewData = validationResult.data;
    const review = {
      ...reviewData,
      published: false,
      // Reviews need approval before being published
      createdAt: (/* @__PURE__ */ new Date()).toISOString()
    };
    const savedReview = await storage.createReview(review);
    return res.status(201).json(savedReview);
  } catch (error) {
    console.error("Error submitting review:", error);
    return res.status(500).json({ message: "Failed to submit review" });
  }
});
router13.get("/job/:jobId", isClientAuthenticated, async (req, res) => {
  try {
    const jobId = parseInt(req.params.jobId);
    if (isNaN(jobId)) {
      return res.status(400).json({ message: "Invalid job ID" });
    }
    const reviews2 = await storage.getReviewsByJobId(jobId);
    return res.json(reviews2);
  } catch (error) {
    console.error("Error fetching job reviews:", error);
    return res.status(500).json({ message: "Failed to fetch job reviews" });
  }
});
router13.post("/:reviewId/images", upload6.array("images", 5), async (req, res) => {
  try {
    const reviewId = parseInt(req.params.reviewId);
    if (isNaN(reviewId)) {
      return res.status(400).json({ message: "Invalid review ID" });
    }
    const files = req.files;
    if (!files || files.length === 0) {
      return res.status(400).json({ message: "No images uploaded" });
    }
    const uploadResults = [];
    for (const file of files) {
      try {
        const fileName = `review_${reviewId}_${Date.now()}_${file.originalname.replace(/\s+/g, "_")}`;
        const metadata = {};
        const imageData = await uploadImage(
          file.buffer,
          fileName,
          "website-content" /* WEBSITE_CONTENT */,
          metadata
        );
        if (!imageData) {
          throw new Error("Failed to upload image");
        }
        let imageUrl = "";
        if (typeof imageData === "string") {
          imageUrl = imageData;
        } else if (imageData && typeof imageData === "object") {
          imageUrl = imageData.urlOriginal || "";
        }
        const image = await storage.createImage({
          title: `Review ${reviewId} image`,
          urlOriginal: imageUrl,
          urlPreview: imageUrl,
          jobId: null,
          // Not associated with a job directly
          inPortfolio: false,
          uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
        });
        await storage.addReviewImage(reviewId, image.id);
        uploadResults.push({
          success: true,
          imageId: image.id
        });
      } catch (err) {
        console.error("Error uploading review image:", err);
        uploadResults.push({
          success: false,
          error: "Failed to upload image"
        });
      }
    }
    return res.status(201).json({
      message: "Images processed",
      results: uploadResults
    });
  } catch (error) {
    console.error("Error handling review images:", error);
    return res.status(500).json({ message: "Failed to process review images" });
  }
});
router13.get("/:reviewId/images", async (req, res) => {
  try {
    const reviewId = parseInt(req.params.reviewId);
    if (isNaN(reviewId)) {
      return res.status(400).json({ message: "Invalid review ID" });
    }
    try {
      const images2 = await storage.getReviewImages(reviewId);
      return res.json(images2);
    } catch (err) {
      console.warn("Error accessing review images:", err);
      return res.json([]);
    }
  } catch (error) {
    console.error("Error fetching review images:", error);
    return res.status(500).json({ message: "Failed to fetch review images" });
  }
});
router13.post("/:reviewId/publish", async (req, res) => {
  try {
    const reviewId = parseInt(req.params.reviewId);
    if (isNaN(reviewId)) {
      return res.status(400).json({ message: "Invalid review ID" });
    }
    const publishedReview = await storage.publishReview(reviewId);
    if (!publishedReview) {
      return res.status(404).json({ message: "Review not found" });
    }
    return res.json(publishedReview);
  } catch (error) {
    console.error("Error publishing review:", error);
    return res.status(500).json({ message: "Failed to publish review" });
  }
});
var reviews_default = router13;

// server/routes/auth.ts
import { Router as Router14 } from "express";

// server/lib/recaptcha.ts
async function verifyRecaptcha(token, action) {
  if (process.env.RECAPTCHA_TEST_BYPASS === "true") {
    console.log("[reCAPTCHA] Test bypass enabled, skipping verification");
    return true;
  }
  if (!process.env.RECAPTCHA_SECRET_KEY) {
    console.error("[reCAPTCHA] Secret key not configured");
    return false;
  }
  try {
    const formData = new URLSearchParams();
    formData.append("secret", process.env.RECAPTCHA_SECRET_KEY);
    formData.append("response", token);
    const response = await fetch("https://www.google.com/recaptcha/api/siteverify", {
      method: "POST",
      body: formData,
      headers: {
        "Content-Type": "application/x-www-form-urlencoded"
      }
    });
    const data = await response.json();
    if (!data.success) {
      console.error("[reCAPTCHA] Verification failed:", data.error_codes);
      return false;
    }
    if (data.score !== void 0 && data.score < 0.5) {
      console.warn(`[reCAPTCHA] Score too low: ${data.score}`);
      return false;
    }
    if (action && data.action && data.action !== action) {
      console.warn(`[reCAPTCHA] Action mismatch: expected ${action}, got ${data.action}`);
      return false;
    }
    return true;
  } catch (error) {
    console.error("[reCAPTCHA] Verification error:", error);
    return false;
  }
}

// server/routes/auth.ts
var router14 = Router14();
router14.post("/verify-recaptcha", async (req, res) => {
  const { token, action } = req.body;
  if (!token) {
    return res.status(400).json({
      success: false,
      message: "Token is required"
    });
  }
  try {
    const isValid = await verifyRecaptcha(token, action);
    if (isValid) {
      return res.status(200).json({
        success: true
      });
    } else {
      return res.status(400).json({
        success: false,
        message: "reCAPTCHA verification failed"
      });
    }
  } catch (error) {
    console.error("reCAPTCHA verification error:", error);
    return res.status(500).json({
      success: false,
      message: "An error occurred during verification"
    });
  }
});
var auth_default = router14;

// server/routes/moodboards.ts
init_storage();
import { Router as Router15 } from "express";
init_schema();
import { z as z5 } from "zod";
var router15 = Router15();
router15.get("/mine", isAuthenticated, async (req, res) => {
  try {
    const userId = req.user?.id;
    if (!userId) {
      return res.status(401).json({ message: "Unauthorized" });
    }
    const moodBoards2 = await storage.getMoodBoardsByUserId(userId);
    return res.json(moodBoards2);
  } catch (error) {
    console.error("Error retrieving mood boards:", error);
    return res.status(500).json({ message: "Failed to retrieve mood boards" });
  }
});
router15.get("/:id", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    const userId = req.user?.id;
    if (moodBoard.userId === userId) {
      return res.json(moodBoard);
    }
    const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
    const userIsCollaborator = collaborators.some((c) => c.userId === userId);
    if (moodBoard.public || userIsCollaborator) {
      return res.json(moodBoard);
    }
    return res.status(403).json({ message: "You do not have access to this mood board" });
  } catch (error) {
    console.error("Error retrieving mood board:", error);
    return res.status(500).json({ message: "Failed to retrieve mood board" });
  }
});
router15.get("/job/:jobId", isAuthenticated, async (req, res) => {
  try {
    const { jobId } = req.params;
    const moodBoards2 = await storage.getMoodBoardsByJobId(parseInt(jobId));
    return res.json(moodBoards2);
  } catch (error) {
    console.error("Error retrieving mood boards for job:", error);
    return res.status(500).json({ message: "Failed to retrieve mood boards for job" });
  }
});
router15.post("/", isAuthenticated, async (req, res) => {
  try {
    const userId = req.user?.id;
    if (!userId) {
      return res.status(401).json({ message: "Unauthorized" });
    }
    const validatedData = insertMoodBoardSchema.parse({
      ...req.body,
      userId
    });
    const newMoodBoard = await storage.createMoodBoard(validatedData);
    await storage.addCollaborator({
      moodBoardId: newMoodBoard.id,
      userId,
      role: "owner"
    });
    return res.status(201).json(newMoodBoard);
  } catch (error) {
    if (error instanceof z5.ZodError) {
      return res.status(400).json({ message: "Invalid mood board data", errors: error.errors });
    }
    console.error("Error creating mood board:", error);
    return res.status(500).json({ message: "Failed to create mood board" });
  }
});
router15.patch("/:id", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const userId = req.user?.id;
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner" && userRole !== "editor") {
        return res.status(403).json({ message: "You do not have permission to update this mood board" });
      }
    }
    const validatedData = insertMoodBoardSchema.partial().parse(req.body);
    const updatedMoodBoard = await storage.updateMoodBoard(parseInt(id), validatedData);
    return res.json(updatedMoodBoard);
  } catch (error) {
    if (error instanceof z5.ZodError) {
      return res.status(400).json({ message: "Invalid mood board data", errors: error.errors });
    }
    console.error("Error updating mood board:", error);
    return res.status(500).json({ message: "Failed to update mood board" });
  }
});
router15.delete("/:id", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const userId = req.user?.id;
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner") {
        return res.status(403).json({ message: "You do not have permission to delete this mood board" });
      }
    }
    const success = await storage.deleteMoodBoard(parseInt(id));
    if (success) {
      return res.status(204).end();
    } else {
      return res.status(500).json({ message: "Failed to delete mood board" });
    }
  } catch (error) {
    console.error("Error deleting mood board:", error);
    return res.status(500).json({ message: "Failed to delete mood board" });
  }
});
router15.get("/:id/items", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const items = await storage.getMoodBoardItems(parseInt(id));
    return res.json(items);
  } catch (error) {
    console.error("Error retrieving mood board items:", error);
    return res.status(500).json({ message: "Failed to retrieve mood board items" });
  }
});
router15.post("/:id/items", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const userId = req.user?.id;
    if (!userId) {
      return res.status(401).json({ message: "Unauthorized" });
    }
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner" && userRole !== "editor") {
        return res.status(403).json({ message: "You do not have permission to add items to this mood board" });
      }
    }
    const validatedData = insertMoodBoardItemSchema.parse({
      ...req.body,
      moodBoardId: parseInt(id),
      addedBy: userId
    });
    const newItem = await storage.createMoodBoardItem(validatedData);
    return res.status(201).json(newItem);
  } catch (error) {
    if (error instanceof z5.ZodError) {
      return res.status(400).json({ message: "Invalid mood board item data", errors: error.errors });
    }
    console.error("Error adding mood board item:", error);
    return res.status(500).json({ message: "Failed to add mood board item" });
  }
});
router15.patch("/:boardId/items/:itemId", isAuthenticated, async (req, res) => {
  try {
    const { boardId, itemId } = req.params;
    const userId = req.user?.id;
    const moodBoard = await storage.getMoodBoard(parseInt(boardId));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(boardId));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner" && userRole !== "editor") {
        return res.status(403).json({ message: "You do not have permission to update items in this mood board" });
      }
    }
    const validatedData = insertMoodBoardItemSchema.partial().parse(req.body);
    const updatedItem = await storage.updateMoodBoardItem(parseInt(itemId), validatedData);
    if (!updatedItem) {
      return res.status(404).json({ message: "Mood board item not found" });
    }
    return res.json(updatedItem);
  } catch (error) {
    if (error instanceof z5.ZodError) {
      return res.status(400).json({ message: "Invalid mood board item data", errors: error.errors });
    }
    console.error("Error updating mood board item:", error);
    return res.status(500).json({ message: "Failed to update mood board item" });
  }
});
router15.delete("/:boardId/items/:itemId", isAuthenticated, async (req, res) => {
  try {
    const { boardId, itemId } = req.params;
    const userId = req.user?.id;
    const moodBoard = await storage.getMoodBoard(parseInt(boardId));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(boardId));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner" && userRole !== "editor") {
        return res.status(403).json({ message: "You do not have permission to delete items from this mood board" });
      }
    }
    const success = await storage.deleteMoodBoardItem(parseInt(itemId));
    if (success) {
      return res.status(204).end();
    } else {
      return res.status(500).json({ message: "Failed to delete mood board item" });
    }
  } catch (error) {
    console.error("Error deleting mood board item:", error);
    return res.status(500).json({ message: "Failed to delete mood board item" });
  }
});
router15.get("/:id/collaborators", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
    return res.json(collaborators);
  } catch (error) {
    console.error("Error retrieving mood board collaborators:", error);
    return res.status(500).json({ message: "Failed to retrieve mood board collaborators" });
  }
});
router15.post("/:id/collaborators", isAuthenticated, async (req, res) => {
  try {
    const { id } = req.params;
    const userId = req.user?.id;
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner") {
        return res.status(403).json({ message: "You do not have permission to add collaborators to this mood board" });
      }
    }
    const validatedData = insertMoodBoardCollaboratorSchema.parse({
      ...req.body,
      moodBoardId: parseInt(id)
    });
    const newCollaborator = await storage.addCollaborator(validatedData);
    return res.status(201).json(newCollaborator);
  } catch (error) {
    if (error instanceof z5.ZodError) {
      return res.status(400).json({ message: "Invalid collaborator data", errors: error.errors });
    }
    console.error("Error adding collaborator:", error);
    return res.status(500).json({ message: "Failed to add collaborator" });
  }
});
router15.patch("/:id/collaborators/:collaboratorId", isAuthenticated, async (req, res) => {
  try {
    const { id, collaboratorId } = req.params;
    const userId = req.user?.id;
    const { role } = req.body;
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner") {
        return res.status(403).json({ message: "You do not have permission to update collaborator roles" });
      }
    }
    if (!role || !["owner", "editor", "viewer"].includes(role)) {
      return res.status(400).json({ message: "Invalid role. Role must be one of: owner, editor, viewer" });
    }
    const updatedCollaborator = await storage.updateCollaboratorRole(parseInt(id), collaboratorId, role);
    if (!updatedCollaborator) {
      return res.status(404).json({ message: "Collaborator not found" });
    }
    return res.json(updatedCollaborator);
  } catch (error) {
    console.error("Error updating collaborator role:", error);
    return res.status(500).json({ message: "Failed to update collaborator role" });
  }
});
router15.delete("/:id/collaborators/:collaboratorId", isAuthenticated, async (req, res) => {
  try {
    const { id, collaboratorId } = req.params;
    const userId = req.user?.id;
    const moodBoard = await storage.getMoodBoard(parseInt(id));
    if (!moodBoard) {
      return res.status(404).json({ message: "Mood board not found" });
    }
    if (moodBoard.userId !== userId) {
      const collaborators = await storage.getMoodBoardCollaborators(parseInt(id));
      const userRole = collaborators.find((c) => c.userId === userId)?.role;
      if (!userRole || userRole !== "owner") {
        return res.status(403).json({ message: "You do not have permission to remove collaborators" });
      }
    }
    const success = await storage.removeCollaborator(parseInt(id), collaboratorId);
    if (success) {
      return res.status(204).end();
    } else {
      return res.status(500).json({ message: "Failed to remove collaborator" });
    }
  } catch (error) {
    console.error("Error removing collaborator:", error);
    return res.status(500).json({ message: "Failed to remove collaborator" });
  }
});
var moodboards_default = router15;

// server/routes/payments/index.ts
init_db();
init_schema();
import express7 from "express";
import { z as z6 } from "zod";
import { eq as eq7 } from "drizzle-orm";
var router16 = express7.Router();
var stripe2;
function initStripe() {
  if (process.env.STRIPE_SECRET_KEY) {
    try {
      import("stripe").then((Stripe2) => {
        stripe2 = new Stripe2.default(process.env.STRIPE_SECRET_KEY, {
          apiVersion: "2023-10-16"
        });
        console.log("Stripe API initialized successfully");
      });
    } catch (error) {
      console.error("Failed to initialize Stripe:", error);
    }
  } else {
    console.warn("Stripe API key not found. Payment functionality will be limited.");
  }
}
var createPaymentIntentSchema = z6.object({
  jobId: z6.number(),
  amount: z6.number().positive(),
  paymentType: z6.enum(["deposit", "balance", "full"]),
  paymentMethod: z6.enum(["credit_card", "bank_transfer", "cash"])
});
async function createPaymentIntent(req, res) {
  try {
    const validatedData = createPaymentIntentSchema.parse(req.body);
    const { jobId, amount, paymentType, paymentMethod } = validatedData;
    if (!stripe2 && paymentMethod === "credit_card") {
      return res.status(503).json({
        error: "Payment service is not available at the moment. Please try again later or contact support."
      });
    }
    const userId = req.user?.id || req.body.userId;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    const [job] = await db.select().from(jobs).where(eq7(jobs.id, jobId));
    if (!job) {
      return res.status(404).json({ error: "Job not found" });
    }
    let paymentIntent;
    let clientSecret;
    if (paymentMethod === "credit_card" && stripe2) {
      paymentIntent = await stripe2.paymentIntents.create({
        amount: Math.round(amount * 100),
        // Convert to cents
        currency: "usd",
        description: `Payment for ${job.title} - ${paymentType}`,
        metadata: {
          jobId: jobId.toString(),
          paymentType,
          userId
        }
      });
      clientSecret = paymentIntent.client_secret;
    }
    const [transaction] = await db.insert(paymentTransactions).values({
      userId,
      jobId,
      amount,
      status: "pending",
      paymentType,
      paymentMethod,
      stripePaymentId: paymentIntent?.id,
      stripeClientSecret: clientSecret,
      metadata: {
        jobTitle: job.title,
        createdBy: userId
      }
    }).returning();
    return res.status(200).json({
      success: true,
      clientSecret,
      transactionId: transaction.id,
      amount
    });
  } catch (error) {
    console.error("Error creating payment intent:", error);
    return res.status(400).json({
      error: error.message || "Failed to process payment"
    });
  }
}
async function confirmPayment(req, res) {
  try {
    const { transactionId } = req.params;
    const { status, paymentMethod } = req.body;
    if (!transactionId || !status) {
      return res.status(400).json({ error: "Missing required fields" });
    }
    if (!req.user || req.user.role !== "admin") {
      return res.status(403).json({ error: "Unauthorized" });
    }
    const [transaction] = await db.select().from(paymentTransactions).where(eq7(paymentTransactions.id, parseInt(transactionId)));
    if (!transaction) {
      return res.status(404).json({ error: "Transaction not found" });
    }
    const [updatedTransaction] = await db.update(paymentTransactions).set({
      status,
      updatedAt: /* @__PURE__ */ new Date()
    }).where(eq7(paymentTransactions.id, parseInt(transactionId))).returning();
    if (status === "succeeded" && transaction.jobId) {
      const [job] = await db.select().from(jobs).where(eq7(jobs.id, transaction.jobId));
      if (job) {
        let updateData = {};
        if (transaction.paymentType === "deposit") {
          updateData.depositPaid = true;
        } else if (transaction.paymentType === "balance") {
          updateData.balancePaid = true;
        } else if (transaction.paymentType === "full") {
          updateData.depositPaid = true;
          updateData.balancePaid = true;
        }
        await db.update(jobs).set(updateData).where(eq7(jobs.id, transaction.jobId));
      }
    }
    return res.status(200).json({
      success: true,
      transaction: updatedTransaction
    });
  } catch (error) {
    console.error("Error confirming payment:", error);
    return res.status(400).json({
      error: error.message || "Failed to confirm payment"
    });
  }
}
async function getPaymentHistory(req, res) {
  try {
    const userId = req.user?.id;
    const { jobId } = req.query;
    if (!userId) {
      return res.status(401).json({ error: "User not authenticated" });
    }
    let query = db.select().from(paymentTransactions).where(eq7(paymentTransactions.userId, userId));
    if (jobId) {
      query = query.where(eq7(paymentTransactions.jobId, parseInt(jobId)));
    }
    const transactions = await query;
    return res.status(200).json(transactions);
  } catch (error) {
    console.error("Error retrieving payment history:", error);
    return res.status(400).json({
      error: error.message || "Failed to retrieve payment history"
    });
  }
}
async function getAllPayments(req, res) {
  try {
    if (!req.user || req.user.role !== "admin") {
      return res.status(403).json({ error: "Unauthorized" });
    }
    const transactions = await db.select().from(paymentTransactions).orderBy(paymentTransactions.createdAt);
    return res.status(200).json(transactions);
  } catch (error) {
    console.error("Error retrieving all payments:", error);
    return res.status(400).json({
      error: error.message || "Failed to retrieve payments"
    });
  }
}
initStripe();
router16.post("/create-intent", isAuthenticated, createPaymentIntent);
router16.post("/:transactionId/confirm", isAdmin, confirmPayment);
router16.get("/history", isAuthenticated, getPaymentHistory);
router16.get("/job/:jobId", isAuthenticated, (req, res) => {
  req.query.jobId = req.params.jobId;
  return getPaymentHistory(req, res);
});
router16.get("/admin/all", isAdmin, getAllPayments);
router16.post("/webhook", (req, res) => {
  res.status(200).send({ received: true });
});
var payments_default = router16;

// server/routes/email-templates.ts
init_db();
import express8 from "express";
var router17 = express8.Router();
router17.get("/", isAuthenticated, async (req, res) => {
  try {
    const result = await pool.query(
      `SELECT * FROM email_templates ORDER BY name ASC`
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching email templates:", error);
    res.status(500).json({ message: "Error fetching email templates" });
  }
});
router17.get("/stage/:stage", isAuthenticated, async (req, res) => {
  const { stage } = req.params;
  try {
    const result = await pool.query(
      `SELECT * FROM email_templates 
       WHERE stage = $1 OR stage IS NULL
       ORDER BY name ASC`,
      [stage]
    );
    res.json(result.rows);
  } catch (error) {
    console.error(`Error fetching templates for stage ${stage}:`, error);
    res.status(500).json({ message: "Error fetching stage templates" });
  }
});
router17.get("/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  try {
    const result = await pool.query(
      `SELECT * FROM email_templates WHERE id = $1`,
      [id]
    );
    if (result.rows.length === 0) {
      return res.status(404).json({ message: "Template not found" });
    }
    res.json(result.rows[0]);
  } catch (error) {
    console.error(`Error fetching template ${id}:`, error);
    res.status(500).json({ message: "Error fetching email template" });
  }
});
router17.post("/", isAdmin, async (req, res) => {
  const {
    name,
    subject,
    body,
    stage,
    isDefault,
    language
  } = req.body;
  if (!name || !subject || !body) {
    return res.status(400).json({
      message: "Missing required fields (name, subject, body)"
    });
  }
  try {
    const result = await pool.query(
      `INSERT INTO email_templates (
        name, subject, body, stage, is_default, language, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())
      RETURNING *`,
      [name, subject, body, stage, isDefault || false, language || "en"]
    );
    res.status(201).json(result.rows[0]);
  } catch (error) {
    console.error("Error creating email template:", error);
    res.status(500).json({ message: "Error creating email template" });
  }
});
router17.put("/:id", isAdmin, async (req, res) => {
  const { id } = req.params;
  const {
    name,
    subject,
    body,
    stage,
    isDefault,
    language
  } = req.body;
  if (!name || !subject || !body) {
    return res.status(400).json({
      message: "Missing required fields (name, subject, body)"
    });
  }
  try {
    const result = await pool.query(
      `UPDATE email_templates
       SET name = $1, 
           subject = $2, 
           body = $3, 
           stage = $4,
           is_default = $5,
           language = $6,
           updated_at = NOW()
       WHERE id = $7
       RETURNING *`,
      [name, subject, body, stage, isDefault || false, language || "en", id]
    );
    if (result.rows.length === 0) {
      return res.status(404).json({ message: "Template not found" });
    }
    res.json(result.rows[0]);
  } catch (error) {
    console.error(`Error updating template ${id}:`, error);
    res.status(500).json({ message: "Error updating email template" });
  }
});
router17.delete("/:id", isAdmin, async (req, res) => {
  const { id } = req.params;
  try {
    const result = await pool.query(
      `DELETE FROM email_templates WHERE id = $1 RETURNING id`,
      [id]
    );
    if (result.rows.length === 0) {
      return res.status(404).json({ message: "Template not found" });
    }
    res.json({ message: "Template deleted successfully" });
  } catch (error) {
    console.error(`Error deleting template ${id}:`, error);
    res.status(500).json({ message: "Error deleting email template" });
  }
});
router17.post("/send", isAuthenticated, async (req, res) => {
  const {
    templateId,
    jobId,
    customizations,
    attachments
  } = req.body;
  if (!templateId || !jobId) {
    return res.status(400).json({
      message: "Missing required fields (templateId, jobId)"
    });
  }
  try {
    const templateResult = await pool.query(
      `SELECT * FROM email_templates WHERE id = $1`,
      [templateId]
    );
    if (templateResult.rows.length === 0) {
      return res.status(404).json({ message: "Template not found" });
    }
    const template = templateResult.rows[0];
    const jobResult = await pool.query(
      `SELECT j.*, c.email as client_email, c.name as client_name
       FROM jobs j
       JOIN users c ON j.client_id = c.id
       WHERE j.id = $1`,
      [jobId]
    );
    if (jobResult.rows.length === 0) {
      return res.status(404).json({ message: "Job not found" });
    }
    const job = jobResult.rows[0];
    let subject = template.subject;
    let body = template.body;
    subject = subject.replace(/\{client_name\}/g, job.client_name).replace(/\{job_title\}/g, job.title).replace(/\{job_date\}/g, new Date(job.date).toLocaleDateString());
    body = body.replace(/\{client_name\}/g, job.client_name).replace(/\{job_title\}/g, job.title).replace(/\{job_date\}/g, new Date(job.date).toLocaleDateString());
    if (customizations) {
      for (const [key, value] of Object.entries(customizations)) {
        const placeholder = new RegExp(`\\{${key}\\}`, "g");
        subject = subject.replace(placeholder, value);
        body = body.replace(placeholder, value);
      }
    }
    console.log(`Email would be sent to ${job.client_email}`);
    console.log(`Subject: ${subject}`);
    console.log(`Body: ${body}`);
    const logResult = await pool.query(
      `INSERT INTO email_logs (
        job_id, client_id, subject, body, sent_to, sent_at, status
      ) VALUES ($1, $2, $3, $4, $5, NOW(), $6)
      RETURNING *`,
      [
        jobId,
        job.client_id,
        subject,
        body,
        job.client_email,
        "sent"
        // In a real implementation, this would be dynamically determined
      ]
    );
    res.status(201).json({
      message: "Email sent successfully",
      emailLog: logResult.rows[0]
    });
  } catch (error) {
    console.error("Error sending email:", error);
    res.status(500).json({ message: "Error sending email" });
  }
});
router17.get("/logs/job/:jobId", isAuthenticated, async (req, res) => {
  const { jobId } = req.params;
  try {
    const result = await pool.query(
      `SELECT * FROM email_logs 
       WHERE job_id = $1 
       ORDER BY sent_at DESC`,
      [jobId]
    );
    res.json(result.rows);
  } catch (error) {
    console.error(`Error fetching email logs for job ${jobId}:`, error);
    res.status(500).json({ message: "Error fetching email logs" });
  }
});
var email_templates_default = router17;

// server/routes/job-checklists.ts
init_db();
import express9 from "express";
var router18 = express9.Router();
router18.get("/templates", isAuthenticated, async (req, res) => {
  try {
    const result = await pool.query(
      `SELECT * FROM checklist_templates ORDER BY name ASC`
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching checklist templates:", error);
    res.status(500).json({ message: "Error fetching checklist templates" });
  }
});
router18.get("/templates/stage/:stage", isAuthenticated, async (req, res) => {
  const { stage } = req.params;
  try {
    const result = await pool.query(
      `SELECT * FROM checklist_templates 
       WHERE stage = $1 OR stage IS NULL
       ORDER BY name ASC`,
      [stage]
    );
    res.json(result.rows);
  } catch (error) {
    console.error(`Error fetching templates for stage ${stage}:`, error);
    res.status(500).json({ message: "Error fetching stage templates" });
  }
});
router18.post("/templates", isAdmin, async (req, res) => {
  const {
    name,
    description,
    stage,
    items
  } = req.body;
  if (!name || !items || !items.length) {
    return res.status(400).json({
      message: "Missing required fields (name, items)"
    });
  }
  try {
    await pool.query("BEGIN");
    const templateResult = await pool.query(
      `INSERT INTO checklist_templates (
        name, description, stage, created_at, updated_at
      ) VALUES ($1, $2, $3, NOW(), NOW())
      RETURNING *`,
      [name, description, stage]
    );
    const template = templateResult.rows[0];
    for (const item of items) {
      await pool.query(
        `INSERT INTO checklist_template_items (
          template_id, description, order_num
        ) VALUES ($1, $2, $3)`,
        [template.id, item.description, item.order || 0]
      );
    }
    await pool.query("COMMIT");
    const result = await pool.query(
      `SELECT t.*, 
         (SELECT json_agg(i.*) FROM checklist_template_items i 
          WHERE i.template_id = t.id ORDER BY i.order_num) as items
       FROM checklist_templates t
       WHERE t.id = $1`,
      [template.id]
    );
    res.status(201).json(result.rows[0]);
  } catch (error) {
    await pool.query("ROLLBACK");
    console.error("Error creating checklist template:", error);
    res.status(500).json({ message: "Error creating checklist template" });
  }
});
router18.put("/templates/:id", isAdmin, async (req, res) => {
  const { id } = req.params;
  const {
    name,
    description,
    stage,
    items
  } = req.body;
  if (!name || !items || !items.length) {
    return res.status(400).json({
      message: "Missing required fields (name, items)"
    });
  }
  try {
    await pool.query("BEGIN");
    await pool.query(
      `UPDATE checklist_templates
       SET name = $1,
           description = $2,
           stage = $3,
           updated_at = NOW()
       WHERE id = $4`,
      [name, description, stage, id]
    );
    await pool.query(
      `DELETE FROM checklist_template_items WHERE template_id = $1`,
      [id]
    );
    for (const item of items) {
      await pool.query(
        `INSERT INTO checklist_template_items (
          template_id, description, order_num
        ) VALUES ($1, $2, $3)`,
        [id, item.description, item.order || 0]
      );
    }
    await pool.query("COMMIT");
    const result = await pool.query(
      `SELECT t.*, 
         (SELECT json_agg(i.*) FROM checklist_template_items i 
          WHERE i.template_id = t.id ORDER BY i.order_num) as items
       FROM checklist_templates t
       WHERE t.id = $1`,
      [id]
    );
    res.json(result.rows[0]);
  } catch (error) {
    await pool.query("ROLLBACK");
    console.error(`Error updating template ${id}:`, error);
    res.status(500).json({ message: "Error updating checklist template" });
  }
});
router18.delete("/templates/:id", isAdmin, async (req, res) => {
  const { id } = req.params;
  try {
    await pool.query("BEGIN");
    await pool.query(
      `DELETE FROM checklist_template_items WHERE template_id = $1`,
      [id]
    );
    await pool.query(
      `DELETE FROM checklist_templates WHERE id = $1`,
      [id]
    );
    await pool.query("COMMIT");
    res.json({ message: "Template deleted successfully" });
  } catch (error) {
    await pool.query("ROLLBACK");
    console.error(`Error deleting template ${id}:`, error);
    res.status(500).json({ message: "Error deleting template" });
  }
});
router18.get("/job/:jobId", isAuthenticated, async (req, res) => {
  const { jobId } = req.params;
  try {
    const result = await pool.query(
      `SELECT c.*, 
         (SELECT json_agg(i.*) FROM checklist_items i 
          WHERE i.checklist_id = c.id ORDER BY i.order_num) as items
       FROM checklists c
       WHERE c.job_id = $1
       ORDER BY c.created_at ASC`,
      [jobId]
    );
    res.json(result.rows);
  } catch (error) {
    console.error(`Error fetching checklists for job ${jobId}:`, error);
    res.status(500).json({ message: "Error fetching job checklists" });
  }
});
router18.get("/job/:jobId/stage/:stage", isAuthenticated, async (req, res) => {
  const { jobId, stage } = req.params;
  try {
    const result = await pool.query(
      `SELECT c.*, 
         (SELECT json_agg(i.*) FROM checklist_items i 
          WHERE i.checklist_id = c.id ORDER BY i.order_num) as items
       FROM checklists c
       WHERE c.job_id = $1 AND c.stage = $2
       ORDER BY c.created_at ASC`,
      [jobId, stage]
    );
    res.json(result.rows);
  } catch (error) {
    console.error(`Error fetching stage checklists for job ${jobId}:`, error);
    res.status(500).json({ message: "Error fetching stage checklists" });
  }
});
router18.post("/job/:jobId", isAuthenticated, async (req, res) => {
  const { jobId } = req.params;
  const {
    name,
    description,
    stage,
    templateId,
    items
  } = req.body;
  if (!name || !stage) {
    return res.status(400).json({
      message: "Missing required fields (name, stage)"
    });
  }
  try {
    await pool.query("BEGIN");
    const checklistResult = await pool.query(
      `INSERT INTO checklists (
        name, description, stage, job_id, template_id, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
      RETURNING *`,
      [name, description, stage, jobId, templateId]
    );
    const checklist = checklistResult.rows[0];
    if (templateId) {
      await pool.query(
        `INSERT INTO checklist_items (
          checklist_id, description, completed, order_num
        )
        SELECT $1, description, false, order_num
        FROM checklist_template_items
        WHERE template_id = $2
        ORDER BY order_num`,
        [checklist.id, templateId]
      );
    } else if (items && items.length) {
      for (const item of items) {
        await pool.query(
          `INSERT INTO checklist_items (
            checklist_id, description, completed, order_num
          ) VALUES ($1, $2, $3, $4)`,
          [checklist.id, item.description, item.completed || false, item.order || 0]
        );
      }
    }
    await pool.query("COMMIT");
    const result = await pool.query(
      `SELECT c.*, 
         (SELECT json_agg(i.*) FROM checklist_items i 
          WHERE i.checklist_id = c.id ORDER BY i.order_num) as items
       FROM checklists c
       WHERE c.id = $1`,
      [checklist.id]
    );
    res.status(201).json(result.rows[0]);
  } catch (error) {
    await pool.query("ROLLBACK");
    console.error("Error creating job checklist:", error);
    res.status(500).json({ message: "Error creating job checklist" });
  }
});
router18.put("/checklists/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  const {
    name,
    description,
    items
  } = req.body;
  try {
    await pool.query("BEGIN");
    await pool.query(
      `UPDATE checklists
       SET name = $1,
           description = $2,
           updated_at = NOW()
       WHERE id = $3`,
      [name, description, id]
    );
    if (items && items.length) {
      for (const item of items) {
        if (item.id) {
          await pool.query(
            `UPDATE checklist_items
             SET description = $1,
                 completed = $2,
                 order_num = $3
             WHERE id = $4 AND checklist_id = $5`,
            [item.description, item.completed, item.order || 0, item.id, id]
          );
        } else {
          await pool.query(
            `INSERT INTO checklist_items (
              checklist_id, description, completed, order_num
            ) VALUES ($1, $2, $3, $4)`,
            [id, item.description, item.completed || false, item.order || 0]
          );
        }
      }
    }
    await pool.query("COMMIT");
    const result = await pool.query(
      `SELECT c.*, 
         (SELECT json_agg(i.*) FROM checklist_items i 
          WHERE i.checklist_id = c.id ORDER BY i.order_num) as items
       FROM checklists c
       WHERE c.id = $1`,
      [id]
    );
    res.json(result.rows[0]);
  } catch (error) {
    await pool.query("ROLLBACK");
    console.error(`Error updating checklist ${id}:`, error);
    res.status(500).json({ message: "Error updating job checklist" });
  }
});
router18.delete("/checklists/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  try {
    await pool.query("BEGIN");
    await pool.query(
      `DELETE FROM checklist_items WHERE checklist_id = $1`,
      [id]
    );
    await pool.query(
      `DELETE FROM checklists WHERE id = $1`,
      [id]
    );
    await pool.query("COMMIT");
    res.json({ message: "Checklist deleted successfully" });
  } catch (error) {
    await pool.query("ROLLBACK");
    console.error(`Error deleting checklist ${id}:`, error);
    res.status(500).json({ message: "Error deleting checklist" });
  }
});
router18.patch("/items/:id/toggle", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  try {
    const result = await pool.query(
      `UPDATE checklist_items
       SET completed = NOT completed
       WHERE id = $1
       RETURNING *`,
      [id]
    );
    if (result.rowCount === 0) {
      return res.status(404).json({ message: "Checklist item not found" });
    }
    res.json(result.rows[0]);
  } catch (error) {
    console.error(`Error toggling checklist item ${id}:`, error);
    res.status(500).json({ message: "Error updating checklist item" });
  }
});
router18.delete("/items/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  try {
    await pool.query(
      `DELETE FROM checklist_items WHERE id = $1`,
      [id]
    );
    res.json({ message: "Checklist item deleted successfully" });
  } catch (error) {
    console.error(`Error deleting checklist item ${id}:`, error);
    res.status(500).json({ message: "Error deleting checklist item" });
  }
});
router18.post("/checklists/:id/items", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  const { description, order } = req.body;
  try {
    const result = await pool.query(
      `INSERT INTO checklist_items (
        checklist_id, description, completed, order_num
      ) VALUES ($1, $2, false, $3)
      RETURNING *`,
      [id, description, order || 0]
    );
    res.status(201).json(result.rows[0]);
  } catch (error) {
    console.error(`Error adding checklist item:`, error);
    res.status(500).json({ message: "Error adding checklist item" });
  }
});
var job_checklists_default = router18;

// server/routes/calendar.ts
init_db();
import express10 from "express";
var router19 = express10.Router();
router19.get("/events", isAuthenticated, async (req, res) => {
  try {
    const result = await pool.query(
      `SELECT * FROM calendar_events ORDER BY start_time ASC`
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching calendar events:", error);
    res.status(500).json({ message: "Error fetching calendar events" });
  }
});
router19.get("/job/:jobId", isAuthenticated, async (req, res) => {
  const { jobId } = req.params;
  try {
    const result = await pool.query(
      `SELECT * FROM calendar_events 
       WHERE job_id = $1 
       ORDER BY start_time ASC`,
      [jobId]
    );
    res.json(result.rows);
  } catch (error) {
    console.error(`Error fetching events for job ${jobId}:`, error);
    res.status(500).json({ message: "Error fetching job events" });
  }
});
router19.get("/events/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  try {
    const result = await pool.query(
      `SELECT * FROM calendar_events WHERE id = $1`,
      [id]
    );
    if (result.rows.length === 0) {
      return res.status(404).json({ message: "Event not found" });
    }
    res.json(result.rows[0]);
  } catch (error) {
    console.error(`Error fetching event ${id}:`, error);
    res.status(500).json({ message: "Error fetching event" });
  }
});
router19.post("/events", isAuthenticated, async (req, res) => {
  const {
    title,
    description,
    start_time,
    end_time,
    location,
    type,
    job_id,
    notifyClient,
    notificationMessage
  } = req.body;
  if (!title || !start_time || !end_time || !type || !job_id) {
    return res.status(400).json({
      message: "Missing required fields (title, start_time, end_time, type, job_id)"
    });
  }
  try {
    const result = await pool.query(
      `INSERT INTO calendar_events (
        title, description, start_time, end_time, 
        location, type, job_id, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
      RETURNING *`,
      [title, description, start_time, end_time, location, type, job_id]
    );
    const newEvent = result.rows[0];
    if (notifyClient && notificationMessage) {
      const jobResult = await pool.query(
        `SELECT c.email 
         FROM jobs j 
         JOIN clients c ON j.client_id = c.id
         WHERE j.id = $1`,
        [job_id]
      );
      if (jobResult.rows.length > 0) {
        const clientEmail = jobResult.rows[0].email;
        await pool.query(
          `INSERT INTO email_logs (
            job_id, client_id, subject, body, sent_to, sent_at, status
          ) VALUES (
            $1, 
            (SELECT client_id FROM jobs WHERE id = $1), 
            $2, 
            $3, 
            $4, 
            NOW(), 
            'sent'
          )`,
          [
            job_id,
            `Event scheduled: ${title}`,
            notificationMessage,
            clientEmail
          ]
        );
        console.log(`Notification email would be sent to ${clientEmail}`);
      }
    }
    res.status(201).json(newEvent);
  } catch (error) {
    console.error("Error creating calendar event:", error);
    res.status(500).json({ message: "Error creating calendar event" });
  }
});
router19.put("/events/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  const {
    title,
    description,
    start_time,
    end_time,
    location,
    type,
    notifyClient,
    notificationMessage
  } = req.body;
  if (!title || !start_time || !end_time || !type) {
    return res.status(400).json({
      message: "Missing required fields (title, start_time, end_time, type)"
    });
  }
  try {
    const checkResult = await pool.query(
      `SELECT job_id FROM calendar_events WHERE id = $1`,
      [id]
    );
    if (checkResult.rows.length === 0) {
      return res.status(404).json({ message: "Event not found" });
    }
    const job_id = checkResult.rows[0].job_id;
    const result = await pool.query(
      `UPDATE calendar_events
       SET title = $1, 
           description = $2, 
           start_time = $3, 
           end_time = $4, 
           location = $5, 
           type = $6,
           updated_at = NOW()
       WHERE id = $7
       RETURNING *`,
      [title, description, start_time, end_time, location, type, id]
    );
    const updatedEvent = result.rows[0];
    if (notifyClient && notificationMessage) {
      const jobResult = await pool.query(
        `SELECT c.email 
         FROM jobs j 
         JOIN clients c ON j.client_id = c.id
         WHERE j.id = $1`,
        [job_id]
      );
      if (jobResult.rows.length > 0) {
        const clientEmail = jobResult.rows[0].email;
        await pool.query(
          `INSERT INTO email_logs (
            job_id, client_id, subject, body, sent_to, sent_at, status
          ) VALUES (
            $1, 
            (SELECT client_id FROM jobs WHERE id = $1), 
            $2, 
            $3, 
            $4, 
            NOW(), 
            'sent'
          )`,
          [
            job_id,
            `Event updated: ${title}`,
            notificationMessage,
            clientEmail
          ]
        );
        console.log(`Update notification email would be sent to ${clientEmail}`);
      }
    }
    res.json(updatedEvent);
  } catch (error) {
    console.error(`Error updating event ${id}:`, error);
    res.status(500).json({ message: "Error updating event" });
  }
});
router19.delete("/events/:id", isAuthenticated, async (req, res) => {
  const { id } = req.params;
  try {
    const checkResult = await pool.query(
      `SELECT * FROM calendar_events WHERE id = $1`,
      [id]
    );
    if (checkResult.rows.length === 0) {
      return res.status(404).json({ message: "Event not found" });
    }
    await pool.query(
      `DELETE FROM calendar_events WHERE id = $1`,
      [id]
    );
    res.json({ message: "Event deleted successfully" });
  } catch (error) {
    console.error(`Error deleting event ${id}:`, error);
    res.status(500).json({ message: "Error deleting event" });
  }
});
router19.get("/range", isAuthenticated, async (req, res) => {
  const { start, end } = req.query;
  if (!start || !end) {
    return res.status(400).json({ message: "Start and end dates are required" });
  }
  try {
    const result = await pool.query(
      `SELECT * FROM calendar_events 
       WHERE start_time >= $1 AND end_time <= $2
       ORDER BY start_time ASC`,
      [start, end]
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching events in date range:", error);
    res.status(500).json({ message: "Error fetching events in date range" });
  }
});
router19.get("/upcoming", isAuthenticated, async (req, res) => {
  const limit = req.query.limit ? parseInt(req.query.limit) : 5;
  try {
    const result = await pool.query(
      `SELECT e.*, j.title as job_title 
       FROM calendar_events e
       JOIN jobs j ON e.job_id = j.id
       WHERE e.start_time >= NOW()
       ORDER BY e.start_time ASC
       LIMIT $1`,
      [limit]
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching upcoming events:", error);
    res.status(500).json({ message: "Error fetching upcoming events" });
  }
});
router19.get("/stats/types", isAuthenticated, isAdmin, async (req, res) => {
  try {
    const result = await pool.query(
      `SELECT type, COUNT(*) as count 
       FROM calendar_events 
       GROUP BY type 
       ORDER BY count DESC`
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching event type statistics:", error);
    res.status(500).json({ message: "Error fetching event type statistics" });
  }
});
router19.get("/stats/months", isAuthenticated, isAdmin, async (req, res) => {
  try {
    const result = await pool.query(
      `SELECT 
         EXTRACT(YEAR FROM start_time) as year,
         EXTRACT(MONTH FROM start_time) as month,
         COUNT(*) as count 
       FROM calendar_events 
       WHERE start_time >= NOW() - INTERVAL '1 year'
       GROUP BY EXTRACT(YEAR FROM start_time), EXTRACT(MONTH FROM start_time)
       ORDER BY year, month`
    );
    res.json(result.rows);
  } catch (error) {
    console.error("Error fetching monthly event statistics:", error);
    res.status(500).json({ message: "Error fetching monthly event statistics" });
  }
});
var calendar_default = router19;

// server/routes.ts
async function registerRoutes(app2) {
  const httpServer = createServer(app2);
  const wss = new WebSocketServer({ server: httpServer, path: "/ws" });
  const moodBoardClients = /* @__PURE__ */ new Map();
  wss.on("connection", (ws2) => {
    let userId = null;
    let moodBoardId = null;
    ws2.on("message", async (message) => {
      try {
        const data = JSON.parse(message.toString());
        if (data.type === "auth" && data.userId && data.moodBoardId) {
          userId = data.userId;
          moodBoardId = parseInt(data.moodBoardId);
          if (!moodBoardClients.has(moodBoardId)) {
            moodBoardClients.set(moodBoardId, /* @__PURE__ */ new Map());
          }
          if (moodBoardId !== null && userId !== null) {
            moodBoardClients.get(moodBoardId).set(userId, ws2);
            await db.insert(moodBoardUserPresence).values({
              moodBoardId,
              userId,
              lastActive: /* @__PURE__ */ new Date(),
              isTyping: false
            }).onConflictDoUpdate({
              target: [moodBoardUserPresence.moodBoardId, moodBoardUserPresence.userId],
              set: {
                lastActive: /* @__PURE__ */ new Date(),
                isTyping: false
              }
            });
            const userResults = await db.select({
              id: users.id,
              name: users.name,
              image: users.image
            }).from(users).where(eq9(users.id, userId));
            const userInfo = userResults.length > 0 ? userResults[0] : null;
            broadcastToMoodBoard(moodBoardId, {
              type: "user_joined",
              user: userInfo,
              timestamp: (/* @__PURE__ */ new Date()).toISOString()
            }, userId);
            const activeUsers = await db.select({
              id: users.id,
              name: users.name,
              image: users.image,
              status: moodBoardUserPresence.status
            }).from(moodBoardUserPresence).innerJoin(users, eq9(users.id, moodBoardUserPresence.userId)).where(eq9(moodBoardUserPresence.moodBoardId, moodBoardId)).execute();
            ws2.send(JSON.stringify({
              type: "active_users",
              users: activeUsers,
              timestamp: (/* @__PURE__ */ new Date()).toISOString()
            }));
            const recentComments = await db.select({
              id: moodBoardComments.id,
              text: moodBoardComments.text,
              createdAt: moodBoardComments.createdAt,
              userId: moodBoardComments.userId,
              userName: users.name,
              userImage: users.image
            }).from(moodBoardComments).innerJoin(users, eq9(users.id, moodBoardComments.userId)).where(eq9(moodBoardComments.moodBoardId, moodBoardId)).orderBy(desc3(moodBoardComments.createdAt)).limit(50).execute();
            ws2.send(JSON.stringify({
              type: "recent_comments",
              comments: recentComments,
              timestamp: (/* @__PURE__ */ new Date()).toISOString()
            }));
          }
        } else if (data.type === "item_update" && data.itemId && moodBoardId !== null && userId !== null) {
          await db.update(moodBoardUserPresence).set({ lastActiveAt: /* @__PURE__ */ new Date() }).where(
            and5(
              eq9(moodBoardUserPresence.moodBoardId, moodBoardId),
              eq9(moodBoardUserPresence.userId, userId)
            )
          );
          broadcastToMoodBoard(moodBoardId, {
            type: "item_update",
            itemId: data.itemId,
            changes: data.changes,
            userId,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          }, userId);
        } else if (data.type === "new_comment" && data.text && moodBoardId !== null && userId !== null) {
          await db.update(moodBoardUserPresence).set({ lastActiveAt: /* @__PURE__ */ new Date() }).where(
            and5(
              eq9(moodBoardUserPresence.moodBoardId, moodBoardId),
              eq9(moodBoardUserPresence.userId, userId)
            )
          );
          const insertResult = await db.insert(moodBoardComments).values({
            moodBoardId,
            userId,
            text: data.text
          }).returning();
          const comment = insertResult.length > 0 ? insertResult[0] : null;
          const userResults = await db.select({
            name: users.name,
            image: users.image
          }).from(users).where(eq9(users.id, userId));
          const userInfo = userResults.length > 0 ? userResults[0] : { name: null, image: null };
          broadcastToMoodBoard(moodBoardId, {
            type: "new_comment",
            comment: {
              ...comment,
              userName: userInfo.name,
              userImage: userInfo.image
            },
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          });
        } else if (data.type === "typing" && moodBoardId !== null && userId !== null) {
          broadcastToMoodBoard(moodBoardId, {
            type: "user_typing",
            userId,
            isTyping: data.isTyping,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          }, userId);
        } else if (data.type === "heartbeat" && moodBoardId !== null && userId !== null) {
          await db.update(moodBoardUserPresence).set({ lastActiveAt: /* @__PURE__ */ new Date() }).where(
            and5(
              eq9(moodBoardUserPresence.moodBoardId, moodBoardId),
              eq9(moodBoardUserPresence.userId, userId)
            )
          );
        }
      } catch (error) {
        console.error("WebSocket message error:", error);
      }
    });
    ws2.on("close", () => {
      if (moodBoardId !== null && userId !== null) {
        moodBoardClients.get(moodBoardId)?.delete(userId);
        db.update(moodBoardUserPresence).set({ status: "offline" }).where(
          and5(
            eq9(moodBoardUserPresence.moodBoardId, moodBoardId),
            eq9(moodBoardUserPresence.userId, userId)
          )
        ).then(() => {
          if (moodBoardId !== null) {
            const clients = moodBoardClients.get(moodBoardId);
            if (clients && clients.size === 0) {
              moodBoardClients.delete(moodBoardId);
            } else if (userId !== null) {
              broadcastToMoodBoard(moodBoardId, {
                type: "user_left",
                userId,
                timestamp: (/* @__PURE__ */ new Date()).toISOString()
              }, userId);
            }
          }
        }).catch((err) => {
          console.error("Error updating user presence on disconnect:", err);
        });
      }
    });
  });
  function broadcastToMoodBoard(moodBoardId, data, excludeUserId) {
    const clients = moodBoardClients.get(moodBoardId);
    if (clients) {
      clients.forEach((client, clientUserId) => {
        if (excludeUserId && clientUserId === excludeUserId) return;
        if (client.readyState === WebSocket.OPEN) {
          client.send(JSON.stringify(data));
        }
      });
    }
  }
  await setupAuth(app2);
  setupClientAuth(app2);
  app2.use("/api/uploads", uploads_default);
  app2.use("/api/images", images_default);
  app2.use("/api/client-data", client_data_default);
  app2.use("/api/portfolio", portfolio_default);
  app2.use("/api/email-templates", email_templates_default);
  app2.use("/api/job-checklists", job_checklists_default);
  app2.use("/api/calendar", calendar_default);
  app2.use("/api/client", client_default);
  app2.use("/api/booking", booking_default);
  app2.use("/api/admin/content", admin_content_default);
  app2.use("/api/user", user_profile_default);
  app2.use("/api/jobs", job_members_default);
  app2.use("/api/reviews", reviews_default);
  app2.use("/api/auth", auth_default);
  app2.use("/api/moodboards", moodboards_default);
  app2.use("/api/payments", payments_default);
  app2.use("/api", await Promise.resolve().then(() => (init_profile(), profile_exports)).then((m) => m.default));
  app2.use("/api/blog", blog_default);
  app2.use("/api/content", await Promise.resolve().then(() => (init_content(), content_exports)).then((m) => m.default));
  app2.get("/api/setup/admin", async (req, res) => {
    try {
      const existingAdmin = await storage.getUserByEmail("admin@example.com");
      if (existingAdmin) {
        return res.json({
          message: "Admin user already exists",
          email: "admin@example.com",
          password: "admin123"
        });
      }
      const hashedPassword = await bcrypt3.hash("admin123", 10);
      const admin = await storage.createUser({
        id: uuidv44(),
        name: "Administrator",
        email: "admin@example.com",
        password: hashedPassword,
        role: "admin",
        locale: "en"
      });
      return res.json({
        message: "Admin user created successfully",
        email: "admin@example.com",
        password: "admin123"
      });
    } catch (error) {
      console.error("Error creating admin user:", error);
      return res.status(500).json({ message: "Failed to create admin user" });
    }
  });
  app2.use("/uploads", express12.static(path7.join(process.cwd(), "uploads")));
  app2.use("/api", images_default);
  app2.use("/api", uploads_default);
  app2.use("/api", client_data_default);
  app2.use("/api", portfolio_default);
  app2.use("/api", client_default);
  app2.use("/api/checkout", checkout_default);
  app2.use("/api/cart", cart_default);
  app2.post("/api/contact", async (req, res) => {
    try {
      const validatedData = insertContactMessageSchema.parse(req.body);
      const contactMessage = await storage.createContactMessage(validatedData);
      return res.status(201).json({
        message: "Contact message received successfully",
        id: contactMessage.id
      });
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while processing your request"
      });
    }
  });
  app2.get("/api/contact", isAdmin, async (req, res) => {
    try {
      const messages = await storage.getAllContactMessages();
      return res.status(200).json(messages);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching contact messages"
      });
    }
  });
  app2.patch("/api/contact/:id/read", isAdmin, async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const message = await storage.markContactMessageAsRead(id);
      if (!message) {
        return res.status(404).json({ message: "Contact message not found" });
      }
      return res.status(200).json(message);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while updating contact message"
      });
    }
  });
  app2.get("/api/session-types", async (req, res) => {
    try {
      const sessionTypes2 = await storage.getActiveSessionTypes();
      return res.status(200).json(sessionTypes2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching session types"
      });
    }
  });
  app2.post("/api/session-types", isAdmin, async (req, res) => {
    try {
      const validatedData = insertSessionTypeSchema.parse(req.body);
      const sessionType = await storage.createSessionType(validatedData);
      return res.status(201).json(sessionType);
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while creating session type"
      });
    }
  });
  app2.post("/api/jobs", isAuthenticated, async (req, res) => {
    try {
      const validatedData = insertJobSchema.parse(req.body);
      const user = req.user;
      const job = await storage.createJob({
        ...validatedData,
        clientId: user.id,
        status: "lead"
      });
      return res.status(201).json(job);
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while creating job"
      });
    }
  });
  app2.get("/api/jobs/my", isAuthenticated, async (req, res) => {
    try {
      const user = req.user;
      const jobs2 = await storage.getJobsByClientId(user.id);
      return res.status(200).json(jobs2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching jobs"
      });
    }
  });
  app2.get("/api/jobs", isAdmin, async (req, res) => {
    try {
      const status = req.query.status;
      const jobs2 = status ? await storage.getJobsByStatus(status) : await Promise.all(
        ["lead", "booked", "editing", "proofing", "delivered", "completed"].map(async (status2) => ({
          status: status2,
          jobs: await storage.getJobsByStatus(status2)
        }))
      );
      return res.status(200).json(jobs2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching jobs"
      });
    }
  });
  app2.get("/api/jobs/:id", isAuthenticated, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      const user = req.user;
      if (job.clientId !== user.id && user.role !== "admin") {
        return res.status(403).json({ message: "Forbidden" });
      }
      return res.status(200).json(job);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching job"
      });
    }
  });
  app2.get("/api/jobs/:id/public", async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      return res.status(200).json({
        id: job.id,
        title: job.title,
        date: job.date,
        sessionType: job.sessionType,
        status: job.status
      });
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching job"
      });
    }
  });
  app2.patch("/api/jobs/:id/status", isAdmin, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const { status } = req.body;
      const job = await storage.updateJobStatus(jobId, status);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      return res.status(200).json(job);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while updating job status"
      });
    }
  });
  app2.patch("/api/jobs/:id", isAdmin, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const job = await storage.updateJob(jobId, req.body);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      return res.status(200).json(job);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while updating job"
      });
    }
  });
  app2.post("/api/images", isAdmin, async (req, res) => {
    try {
      const validatedData = insertImageSchema.parse(req.body);
      const image = await storage.createImage(validatedData);
      return res.status(201).json(image);
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while uploading image"
      });
    }
  });
  app2.get("/api/jobs/:id/images", isAuthenticated, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      const user = req.user;
      if (job.clientId !== user.id && user.role !== "admin") {
        return res.status(403).json({ message: "Forbidden" });
      }
      const images2 = await storage.getImagesByJobId(jobId);
      return res.status(200).json(images2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching images"
      });
    }
  });
  app2.get("/api/portfolio", async (req, res) => {
    try {
      const images2 = await storage.getPortfolioImages();
      return res.status(200).json(images2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching portfolio images"
      });
    }
  });
  app2.get("/api/portfolio/categories", async (req, res) => {
    try {
      const categories = await storage.getAllPortfolioCategories();
      return res.status(200).json(categories);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching portfolio categories"
      });
    }
  });
  app2.get("/api/portfolio/categories/:id", async (req, res) => {
    try {
      const categoryId = parseInt(req.params.id);
      const images2 = await storage.getPortfolioImagesByCategory(categoryId);
      return res.status(200).json(images2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching category images"
      });
    }
  });
  app2.patch("/api/images/:id/portfolio", isAdmin, async (req, res) => {
    try {
      const imageId = parseInt(req.params.id);
      const image = await storage.toggleImagePortfolioStatus(imageId);
      if (!image) {
        return res.status(404).json({ message: "Image not found" });
      }
      return res.status(200).json(image);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while updating image"
      });
    }
  });
  app2.post("/api/images/:id/categories", isAdmin, async (req, res) => {
    try {
      const imageId = parseInt(req.params.id);
      const { categoryId } = req.body;
      await storage.addImageCategory(imageId, categoryId);
      return res.status(200).json({ message: "Category added successfully" });
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while adding category"
      });
    }
  });
  app2.post("/api/images/:id/comments", isAuthenticated, async (req, res) => {
    try {
      const imageId = parseInt(req.params.id);
      const user = req.user;
      const validatedData = insertImageCommentSchema.parse({
        ...req.body,
        imageId,
        userId: user.id
      });
      const comment = await storage.addImageComment(validatedData);
      return res.status(201).json(comment);
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while adding comment"
      });
    }
  });
  app2.get("/api/images/:id/comments", isAuthenticated, async (req, res) => {
    try {
      const imageId = parseInt(req.params.id);
      const comments = await storage.getImageComments(imageId);
      return res.status(200).json(comments);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching comments"
      });
    }
  });
  app2.post("/api/jobs/:id/reviews", isAuthenticated, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const user = req.user;
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      if (job.clientId !== user.id) {
        return res.status(403).json({ message: "Forbidden" });
      }
      const validatedData = insertReviewSchema.parse({
        ...req.body,
        jobId,
        userId: user.id
      });
      const review = await storage.createReview(validatedData);
      if (review.stars >= 4) {
        await storage.publishReview(review.id);
      }
      const code = `${user.id.substring(0, 6)}-${Math.random().toString(36).substring(2, 8).toUpperCase()}`;
      const referral = await storage.createReferral({
        code,
        ownerUserId: user.id,
        discount: 10
        // 10% discount
      });
      return res.status(201).json({
        ...review,
        referralCode: referral.code,
        referralUrl: `${process.env.WEBSITE_URL || ""}/booking?ref=${referral.code}`
      });
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while submitting review"
      });
    }
  });
  app2.post("/api/jobs/:id/reviews/public", async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const { stars, text: text3, name, email } = req.body;
      if (!stars || stars < 1 || stars > 5) {
        return res.status(400).json({ message: "Invalid star rating" });
      }
      if (!text3 || typeof text3 !== "string") {
        return res.status(400).json({ message: "Review text is required" });
      }
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      const review = await storage.createReview({
        jobId,
        stars,
        text: text3,
        name,
        email,
        userId: null
        // Public review
      });
      if (review.stars >= 4) {
        await storage.publishReview(review.id);
      }
      const code = `REF-${jobId}-${Math.random().toString(36).substring(2, 8).toUpperCase()}`;
      if (!job.clientId) {
        console.warn(`No client ID found for job ${jobId}, using admin user as owner`);
      }
      const clientId = job.clientId || (await storage.getUserByEmail("admin@carolinagoulart.com"))?.id || "";
      const referral = await storage.createReferral({
        code,
        ownerUserId: clientId,
        // Assign to the job client
        discount: 10
        // 10% discount
      });
      return res.status(201).json({
        success: true,
        referralCode: referral.code,
        referralUrl: `${process.env.WEBSITE_URL || ""}/booking?ref=${referral.code}`
      });
    } catch (error) {
      console.error("Error submitting public review:", error);
      return res.status(500).json({
        message: "An error occurred while submitting review"
      });
    }
  });
  app2.get("/api/reviews", async (req, res) => {
    try {
      const reviews2 = await storage.getPublishedReviews();
      return res.status(200).json(reviews2);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while fetching reviews"
      });
    }
  });
  app2.patch("/api/reviews/:id/publish", isAdmin, async (req, res) => {
    try {
      const reviewId = parseInt(req.params.id);
      const review = await storage.publishReview(reviewId);
      if (!review) {
        return res.status(404).json({ message: "Review not found" });
      }
      return res.status(200).json(review);
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while publishing review"
      });
    }
  });
  app2.post("/api/portfolio/categories", isAdmin, async (req, res) => {
    try {
      const validatedData = insertPortfolioCategorySchema.parse(req.body);
      const category = await storage.createPortfolioCategory(validatedData);
      return res.status(201).json(category);
    } catch (error) {
      if (error instanceof ZodError) {
        return res.status(400).json({
          message: "Validation error",
          errors: error.errors
        });
      }
      return res.status(500).json({
        message: "An error occurred while creating category"
      });
    }
  });
  app2.get("/api/referrals/:code", async (req, res) => {
    try {
      const code = req.params.code;
      const referral = await storage.getReferralByCode(code);
      if (!referral) {
        return res.status(404).json({ message: "Referral code not found" });
      }
      if (referral.redeemedByUserId) {
        return res.status(400).json({ message: "Referral code already redeemed" });
      }
      return res.status(200).json({
        code: referral.code,
        discount: referral.discount
      });
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while checking referral code"
      });
    }
  });
  app2.post("/api/referrals/:code/redeem", isAuthenticated, async (req, res) => {
    try {
      const code = req.params.code;
      const user = req.user;
      const referral = await storage.redeemReferral(code, user.id);
      if (!referral) {
        return res.status(404).json({ message: "Referral code not found or already redeemed" });
      }
      return res.status(200).json({
        message: "Referral code redeemed successfully",
        discount: referral.discount
      });
    } catch (error) {
      return res.status(500).json({
        message: "An error occurred while redeeming referral code"
      });
    }
  });
  app2.get("/api/referrals/my-code", isAuthenticated, async (req, res) => {
    try {
      const user = req.user;
      const existingReferrals = await storage.getReferralsByOwner(user.id);
      if (!existingReferrals || existingReferrals.length === 0) {
        const code = `${user.id.substring(0, 6)}-${Math.random().toString(36).substring(2, 8).toUpperCase()}`;
        try {
          const referral2 = await storage.createReferral({
            code,
            ownerUserId: user.id,
            discount: 10
            // 10% discount
          });
          return res.status(200).json({
            code: referral2.code,
            url: `${process.env.WEBSITE_URL || ""}/booking?ref=${referral2.code}`
          });
        } catch (err) {
          console.error("Error creating referral code:", err);
          return res.status(200).json({
            inactive: true,
            message: "Referral codes will be available after completing your first session."
          });
        }
      }
      const referral = existingReferrals[0];
      return res.status(200).json({
        code: referral.code,
        url: `${process.env.WEBSITE_URL || ""}/booking?ref=${referral.code}`
      });
    } catch (error) {
      console.error("Error fetching user's referral code:", error);
      return res.status(200).json({
        inactive: true,
        message: "Referral codes will be available after completing your first session."
      });
    }
  });
  app2.post("/api/jobs/:id/download", isAuthenticated, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const { imageIds } = req.body;
      if (!Array.isArray(imageIds) || imageIds.length === 0) {
        return res.status(400).json({ message: "Image IDs are required" });
      }
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      const user = req.user;
      if (job.clientId !== user.id && user.role !== "admin") {
        return res.status(403).json({ message: "Forbidden" });
      }
      if (job.status !== "delivered" && job.status !== "completed" && user.role !== "admin") {
        return res.status(400).json({
          message: "Images are not ready for download yet. Your gallery is still in the proofing stage."
        });
      }
      console.log(`User ${user.id} downloaded ${imageIds.length} images from job ${jobId}`);
      return res.status(200).json({
        downloadUrl: `/api/download/${jobId}?token=${encodeURIComponent(Date.now().toString())}&type=highres`,
        message: "Download prepared successfully"
      });
    } catch (error) {
      console.error("Error preparing download:", error);
      return res.status(500).json({
        message: "An error occurred while preparing your download"
      });
    }
  });
  app2.post("/api/jobs/:id/download-social", isAuthenticated, async (req, res) => {
    try {
      const jobId = parseInt(req.params.id);
      const { imageIds } = req.body;
      if (!Array.isArray(imageIds) || imageIds.length === 0) {
        return res.status(400).json({ message: "Image IDs are required" });
      }
      const job = await storage.getJob(jobId);
      if (!job) {
        return res.status(404).json({ message: "Job not found" });
      }
      const user = req.user;
      if (job.clientId !== user.id && user.role !== "admin") {
        return res.status(403).json({ message: "Forbidden" });
      }
      if (job.status !== "proofing" && job.status !== "delivered" && job.status !== "completed" && user.role !== "admin") {
        return res.status(400).json({
          message: "Images are not ready for download yet"
        });
      }
      console.log(`User ${user.id} downloaded ${imageIds.length} social media images from job ${jobId}`);
      return res.status(200).json({
        downloadUrl: `/api/download/${jobId}?token=${encodeURIComponent(Date.now().toString())}&type=social`,
        message: "Social media download prepared successfully"
      });
    } catch (error) {
      console.error("Error preparing social download:", error);
      return res.status(500).json({
        message: "An error occurred while preparing your download"
      });
    }
  });
  app2.get("/api/download/:jobId", isAuthenticated, (req, res) => {
    const jobId = req.params.jobId;
    const token = req.query.token;
    const type = req.query.type || "highres";
    if (!token) {
      return res.status(400).json({ message: "Invalid download token" });
    }
    res.set("Content-Type", "text/plain");
    res.send(`This is a placeholder for the ${type} download of job ${jobId}. In a production environment, this would be a ZIP file containing your selected images.`);
  });
  app2.post("/api/client/referral", isAuthenticated, async (req, res) => {
    try {
      const user = req.user;
      const code = `${user.id.substring(0, 6)}-${Math.random().toString(36).substring(2, 8).toUpperCase()}`;
      const referral = await storage.createReferral({
        code,
        ownerUserId: user.id,
        discount: 10
        // 10% discount
      });
      return res.status(201).json({
        code: referral.code,
        discount: referral.discount,
        url: `${process.env.WEBSITE_URL || ""}/booking?ref=${referral.code}`
      });
    } catch (error) {
      console.error("Error creating referral code:", error);
      return res.status(500).json({
        message: "An error occurred while creating referral code"
      });
    }
  });
  app2.patch("/api/admin/images/portfolio", isAdmin, async (req, res) => {
    try {
      const { imageIds, status } = req.body;
      if (!Array.isArray(imageIds) || imageIds.length === 0) {
        return res.status(400).json({ message: "Image IDs are required" });
      }
      await storage.bulkUpdatePortfolioStatus(imageIds, status);
      return res.status(200).json({
        message: `Successfully ${status ? "added" : "removed"} ${imageIds.length} images ${status ? "to" : "from"} portfolio`
      });
    } catch (error) {
      console.error("Error updating portfolio status:", error);
      return res.status(500).json({
        message: "An error occurred while updating portfolio status"
      });
    }
  });
  app2.post("/api/admin/images/categories", isAdmin, async (req, res) => {
    try {
      const { imageIds, categoryId } = req.body;
      if (!Array.isArray(imageIds) || imageIds.length === 0) {
        return res.status(400).json({ message: "Image IDs are required" });
      }
      if (!categoryId) {
        return res.status(400).json({ message: "Category ID is required" });
      }
      await storage.bulkAddToCategory(imageIds, categoryId);
      return res.status(200).json({
        message: `Successfully added ${imageIds.length} images to category`
      });
    } catch (error) {
      console.error("Error adding images to category:", error);
      return res.status(500).json({
        message: "An error occurred while adding images to category"
      });
    }
  });
  return httpServer;
}

// server/vite.ts
import express13 from "express";
import fs6 from "fs";
import path9 from "path";
import { createServer as createViteServer, createLogger } from "vite";

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path8 from "path";
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
var vite_config_default = defineConfig({
  plugins: [
    react(),
    runtimeErrorOverlay(),
    ...process.env.NODE_ENV !== "production" && process.env.REPL_ID !== void 0 ? [
      await import("@replit/vite-plugin-cartographer").then(
        (m) => m.cartographer()
      )
    ] : []
  ],
  resolve: {
    alias: {
      "@": path8.resolve(import.meta.dirname, "client", "src"),
      "@shared": path8.resolve(import.meta.dirname, "shared"),
      "@assets": path8.resolve(import.meta.dirname, "attached_assets")
    }
  },
  root: path8.resolve(import.meta.dirname, "client"),
  build: {
    outDir: path8.resolve(import.meta.dirname, "dist/public"),
    emptyOutDir: true
  }
});

// server/vite.ts
import { nanoid } from "nanoid";
var viteLogger = createLogger();
function log(message, source = "express") {
  const formattedTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
    hour: "numeric",
    minute: "2-digit",
    second: "2-digit",
    hour12: true
  });
  console.log(`${formattedTime} [${source}] ${message}`);
}
async function setupVite(app2, server) {
  const serverOptions = {
    middlewareMode: true,
    hmr: { server },
    allowedHosts: true
  };
  const vite = await createViteServer({
    ...vite_config_default,
    configFile: false,
    customLogger: {
      ...viteLogger,
      error: (msg, options) => {
        viteLogger.error(msg, options);
        process.exit(1);
      }
    },
    server: serverOptions,
    appType: "custom"
  });
  app2.use(vite.middlewares);
  app2.use("*", async (req, res, next) => {
    const url = req.originalUrl;
    try {
      const clientTemplate = path9.resolve(
        import.meta.dirname,
        "..",
        "client",
        "index.html"
      );
      let template = await fs6.promises.readFile(clientTemplate, "utf-8");
      template = template.replace(
        `src="/src/main.tsx"`,
        `src="/src/main.tsx?v=${nanoid()}"`
      );
      const page = await vite.transformIndexHtml(url, template);
      res.status(200).set({ "Content-Type": "text/html" }).end(page);
    } catch (e) {
      vite.ssrFixStacktrace(e);
      next(e);
    }
  });
}
function serveStatic(app2) {
  const distPath = path9.resolve(import.meta.dirname, "public");
  if (!fs6.existsSync(distPath)) {
    throw new Error(
      `Could not find the build directory: ${distPath}, make sure to build the client first`
    );
  }
  app2.use(express13.static(distPath));
  app2.use("*", (_req, res) => {
    res.sendFile(path9.resolve(distPath, "index.html"));
  });
}

// server/index.ts
import helmet from "helmet";
import rateLimit from "express-rate-limit";
import csrf from "csrf";
import { JSDOM } from "jsdom";
import DOMPurify from "dompurify";
import hpp from "hpp";
import cors from "cors";
import * as Sentry2 from "@sentry/node";
var app = express14();
app.set("trust proxy", 1);
var window = new JSDOM("").window;
var domPurifier = DOMPurify(window);
var tokens = new csrf();
if (process.env.NODE_ENV === "production" && process.env.SENTRY_DSN) {
  Sentry2.init({
    dsn: process.env.SENTRY_DSN,
    tracesSampleRate: 1
  });
}
if (process.env.NODE_ENV === "production" && process.env.SENTRY_DSN) {
  app.use((req, res, next) => {
    try {
      next();
    } catch (err) {
      Sentry2.captureException(err);
      throw err;
    }
  });
}
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", "https://source.unsplash.com"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https://source.unsplash.com"],
      connectSrc: ["'self'"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"]
    }
  },
  // Enable HTTP Strict Transport Security
  hsts: {
    maxAge: 63072e3,
    // 2 years in seconds
    includeSubDomains: true,
    preload: true
  },
  crossOriginEmbedderPolicy: false
}));
var limiter = rateLimit({
  windowMs: 15 * 60 * 1e3,
  // 15 minutes
  max: 100,
  // Limit each IP to 100 requests per windowMs
  standardHeaders: true,
  // Return rate limit info in the `RateLimit-*` headers
  legacyHeaders: false,
  // Disable the `X-RateLimit-*` headers
  message: "Too many requests from this IP, please try again after 15 minutes"
});
app.use("/api", limiter);
app.use(cors({
  origin: process.env.NODE_ENV === "production" ? "https://carolinagoulart.com" : "http://localhost:5000",
  // Development domain
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true
}));
app.use(express14.json({
  limit: "1mb",
  // Limit body size
  verify: (req, res, buf) => {
    if (req.url.includes("/api")) {
      req.rawBody = buf;
    }
  }
}));
app.use(express14.urlencoded({ extended: false }));
app.use(hpp());
app.use((req, res, next) => {
  const start = Date.now();
  const path10 = req.path;
  let capturedJsonResponse = void 0;
  const originalResJson = res.json;
  res.json = function(bodyJson, ...args) {
    capturedJsonResponse = bodyJson;
    return originalResJson.apply(res, [bodyJson, ...args]);
  };
  res.on("finish", () => {
    const duration = Date.now() - start;
    if (path10.startsWith("/api")) {
      let logLine = `${req.method} ${path10} ${res.statusCode} in ${duration}ms`;
      if (capturedJsonResponse) {
        logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
      }
      if (logLine.length > 80) {
        logLine = logLine.slice(0, 79) + "\u2026";
      }
      log(logLine);
    }
  });
  next();
});
(async () => {
  const server = await registerRoutes(app);
  app.use((err, _req, res, _next) => {
    const status = err.status || err.statusCode || 500;
    const message = err.message || "Internal Server Error";
    if (process.env.NODE_ENV === "production" && process.env.SENTRY_DSN) {
      Sentry2.captureException(err);
    } else {
      console.error(err);
    }
    res.status(status).json({
      message: process.env.NODE_ENV === "production" ? "An error occurred" : message
      // Detailed message in development
    });
  });
  if (process.env.NODE_ENV === "production") {
    initB2Client().then((client) => {
      if (client) {
        console.log("Backblaze B2 client initialized successfully");
        scheduleBackups();
      } else {
        console.warn("Backblaze B2 client initialization failed");
      }
    });
  }
  if (app.get("env") === "development") {
    await setupVite(app, server);
  } else {
    serveStatic(app);
  }
  const port = 5e3;
  server.listen({
    port,
    host: "0.0.0.0",
    reusePort: true
  }, () => {
    log(`serving on port ${port}`);
  });
})();
