Nicholai b20db98051
Some checks failed
CI / build-and-test (pull_request) Failing after 1m19s
feat(ci,flags,ops): ship end-to-end CI, feature-flag framework, gated surfaces, and ops docs
CI (.gitea/workflows/ci.yaml): lint → typecheck → vitest w/ coverage → OpenNext build → preview smoke → bundle-size budgets; Node 20; npm ci; artifacts; safe env; D1 dry-run scaffold.

Budgets: add scripts/budgets.mjs; TOTAL_STATIC_MAX_BYTES and MAX_ASSET_BYTES thresholds; report top offenders; fail on breach; README CI section.

Flags: add lib/flags.ts with typed booleans and safe defaults (ADMIN_ENABLED, ARTISTS_MODULE_ENABLED, UPLOADS_ADMIN_ENABLED, BOOKING_ENABLED, PUBLIC_APPOINTMENT_REQUESTS_ENABLED, REFERENCE_UPLOADS_PUBLIC_ENABLED, DEPOSITS_ENABLED, PUBLIC_DB_ARTISTS_ENABLED, ADVANCED_NAV_SCROLL_ANIMATIONS_ENABLED, STRICT_CI_GATES_ENABLED, ISR_CACHE_R2_ENABLED); robust parsing; client provider; unit tests.

Wiring: gate Admin shell and admin write APIs (503 JSON on uploads and artists writes); disable booking submit and short-circuit booking mutations when off; render static Hero/Artists when advanced animations off; tests for UI and API guards.

Ops: expand docs/prd/rollback-strategy.md with “Feature Flags Operations,” Cloudflare Dashboard and wrangler.toml steps, preview simulation, incident playbook, and post-toggle smoke checklist.

Release: add docs/releases/2025-09-19-feature-flags-rollout.md with last-good commit, preview/production flag matrices, rollback notes, and smoke results; link from rollback doc.

Chore: fix TS issues (gift-cards boolean handling, Lenis options, tailwind darkMode), remove next-on-pages peer conflict, update package.json scripts, configure Gitea act_runner label, open draft PR to trigger CI.

Refs: CI-1, FF-1, FF-2, FF-3, OPS-1
Impact: defaults preserve current behavior; no runtime changes unless flags flipped
2025-09-19 21:33:09 -06:00

1 line
14 KiB
JavaScript

"use strict";(()=>{var e={};e.id=3196,e.ids=[3196],e.modules={72934:e=>{e.exports=require("next/dist/client/components/action-async-storage.external.js")},54580:e=>{e.exports=require("next/dist/client/components/request-async-storage.external.js")},45869:e=>{e.exports=require("next/dist/client/components/static-generation-async-storage.external.js")},20399:e=>{e.exports=require("next/dist/compiled/next-server/app-page.runtime.prod.js")},30517:e=>{e.exports=require("next/dist/compiled/next-server/app-route.runtime.prod.js")},27790:e=>{e.exports=require("assert")},78893:e=>{e.exports=require("buffer")},84770:e=>{e.exports=require("crypto")},17702:e=>{e.exports=require("events")},32615:e=>{e.exports=require("http")},35240:e=>{e.exports=require("https")},86624:e=>{e.exports=require("querystring")},17360:e=>{e.exports=require("url")},21764:e=>{e.exports=require("util")},71568:e=>{e.exports=require("zlib")},60349:(e,t,i)=>{i.r(t),i.d(t,{originalPathname:()=>x,patchFetch:()=>A,requestAsyncStorage:()=>v,routeModule:()=>b,serverHooks:()=>I,staticGenerationAsyncStorage:()=>f});var r={};i.r(r),i.d(r,{GET:()=>z,POST:()=>c,dynamic:()=>p});var n=i(73278),s=i(45002),a=i(54877),o=i(71309),l=i(33897),u=i(74725),d=i(69362),m=i(1035),g=i(93470);let p="force-dynamic";async function z(e,{params:t}={},i){try{let{searchParams:t}=new URL(e.url),r=d.dC.parse({page:t.get("page")||"1",limit:t.get("limit")||"10"}),n=d.NK.parse({isActive:t.get("isActive"),specialty:t.get("specialty"),search:t.get("search")}),s=await (0,m.fC)(i?.env);if(void 0!==n.isActive&&(s=s.filter(e=>e.isActive===n.isActive)),n.specialty&&(s=s.filter(e=>e.specialties.some(e=>e.toLowerCase().includes(n.specialty.toLowerCase())))),n.search){let e=n.search.toLowerCase();s=s.filter(t=>t.name.toLowerCase().includes(e)||t.bio.toLowerCase().includes(e))}let a=(r.page-1)*r.limit,l=a+r.limit,u=s.slice(a,l);return o.NextResponse.json({artists:u,pagination:{page:r.page,limit:r.limit,total:s.length,totalPages:Math.ceil(s.length/r.limit)},filters:n})}catch(e){return console.error("Error fetching artists:",e),o.NextResponse.json({error:"Failed to fetch artists"},{status:500})}}async function c(e,{params:t}={},i){try{if(!g.vU.ARTISTS_MODULE_ENABLED)return o.NextResponse.json({error:"Artists module disabled"},{status:503});let t=await (0,l.mk)(u.i.SHOP_ADMIN),r=await e.json(),n=d.Jt.parse(r),s=await (0,m.Rw)({...n,userId:t.user.id},i?.env);return o.NextResponse.json(s,{status:201})}catch(e){if(console.error("Error creating artist:",e),e instanceof Error){if(e.message.includes("Authentication required"))return o.NextResponse.json({error:"Authentication required"},{status:401});if(e.message.includes("Insufficient permissions"))return o.NextResponse.json({error:"Insufficient permissions"},{status:403})}return o.NextResponse.json({error:"Failed to create artist"},{status:500})}}let b=new n.AppRouteRouteModule({definition:{kind:s.x.APP_ROUTE,page:"/api/artists/route",pathname:"/api/artists",filename:"route",bundlePath:"app/api/artists/route"},resolvedPagePath:"/home/Nicholai/Documents/Dev/united_v03/united-tattoo/united-tattoo/app/api/artists/route.ts",nextConfigOutput:"standalone",userland:r}),{requestAsyncStorage:v,staticGenerationAsyncStorage:f,serverHooks:I}=b,x="/api/artists/route";function A(){return(0,a.patchFetch)({serverHooks:I,staticGenerationAsyncStorage:f})}},93470:(e,t,i)=>{i.d(t,{L6:()=>l,vU:()=>u});let r=Object.freeze({ADMIN_ENABLED:!0,ARTISTS_MODULE_ENABLED:!0,UPLOADS_ADMIN_ENABLED:!0,BOOKING_ENABLED:!0,PUBLIC_APPOINTMENT_REQUESTS_ENABLED:!1,REFERENCE_UPLOADS_PUBLIC_ENABLED:!1,DEPOSITS_ENABLED:!1,PUBLIC_DB_ARTISTS_ENABLED:!1,ADVANCED_NAV_SCROLL_ANIMATIONS_ENABLED:!0,STRICT_CI_GATES_ENABLED:!0,ISR_CACHE_R2_ENABLED:!0}),n=Object.keys(r),s=new Set(n),a=new Set,o=null;function l(e={}){if(e.refresh&&(o=null),o)return o;let t=function(){let e={};for(let t of n){let i=function(e){let t=function(){if("undefined"!=typeof globalThis)return globalThis.__UNITED_TATTOO_RUNTIME_FLAGS__}();return t&&void 0!==t[e]?t[e]:"undefined"!=typeof process&&process.env&&void 0!==process.env[e]?process.env[e]:void 0}(t),n=function(e,t){if("boolean"==typeof e)return e;if("string"==typeof e){let t=e.trim().toLowerCase();if("true"===t||"1"===t)return!0;if("false"===t||"0"===t)return!1}return t}(i,r[t]);null!=i&&("string"!=typeof i||""!==i.trim())||a.has(t)||(a.add(t),"undefined"!=typeof console&&console.warn(`[flags] ${t} not provided; defaulting to ${n}. Set env var to override.`)),e[t]=n}return Object.freeze(e)}();return o=t,t}let u=new Proxy({},{get:(e,t)=>{if(s.has(t))return l()[t]},ownKeys:()=>n,getOwnPropertyDescriptor:(e,t)=>{if(s.has(t))return{configurable:!0,enumerable:!0,value:l()[t]}}})},69362:(e,t,i)=>{i.d(t,{IF:()=>u,Jt:()=>s,NK:()=>m,dC:()=>d,xD:()=>a});var r=i(29628),n=i(74725);r.z.object({id:r.z.string().uuid(),email:r.z.string().email(),name:r.z.string().min(1,"Name is required"),role:r.z.nativeEnum(n.i),avatar:r.z.string().url().optional()}),r.z.object({email:r.z.string().email("Invalid email address"),name:r.z.string().min(1,"Name is required").max(100,"Name too long"),password:r.z.string().min(8,"Password must be at least 8 characters"),role:r.z.nativeEnum(n.i).default(n.i.CLIENT)}).partial().extend({id:r.z.string().uuid()}),r.z.object({id:r.z.string().uuid(),userId:r.z.string().uuid(),name:r.z.string().min(1,"Artist name is required"),bio:r.z.string().min(10,"Bio must be at least 10 characters"),specialties:r.z.array(r.z.string()).min(1,"At least one specialty is required"),instagramHandle:r.z.string().optional(),isActive:r.z.boolean().default(!0),hourlyRate:r.z.number().positive().optional()});let s=r.z.object({name:r.z.string().min(1,"Artist name is required").max(100,"Name too long"),bio:r.z.string().min(10,"Bio must be at least 10 characters").max(1e3,"Bio too long"),specialties:r.z.array(r.z.string().min(1)).min(1,"At least one specialty is required").max(10,"Too many specialties"),instagramHandle:r.z.string().regex(/^[a-zA-Z0-9._]+$/,"Invalid Instagram handle").optional(),hourlyRate:r.z.number().positive("Hourly rate must be positive").max(1e3,"Hourly rate too high").optional(),isActive:r.z.boolean().default(!0)}),a=s.partial().extend({id:r.z.string().uuid()});r.z.object({id:r.z.string().uuid(),artistId:r.z.string().uuid(),url:r.z.string().url("Invalid image URL"),caption:r.z.string().max(500,"Caption too long").optional(),tags:r.z.array(r.z.string()).max(20,"Too many tags"),order:r.z.number().int().min(0),isPublic:r.z.boolean().default(!0)}),r.z.object({artistId:r.z.string().uuid(),url:r.z.string().url("Invalid image URL"),caption:r.z.string().max(500,"Caption too long").optional(),tags:r.z.array(r.z.string().min(1)).max(20,"Too many tags").default([]),order:r.z.number().int().min(0).default(0),isPublic:r.z.boolean().default(!0)}).partial().extend({id:r.z.string().uuid()}),r.z.object({id:r.z.string().uuid(),artistId:r.z.string().uuid(),clientId:r.z.string().uuid(),title:r.z.string().min(1,"Title is required"),description:r.z.string().optional(),startTime:r.z.date(),endTime:r.z.date(),status:r.z.nativeEnum(n.Z),depositAmount:r.z.number().positive().optional(),totalAmount:r.z.number().positive().optional(),notes:r.z.string().optional()}),r.z.object({artistId:r.z.string().uuid("Invalid artist ID"),clientId:r.z.string().uuid("Invalid client ID"),title:r.z.string().min(1,"Title is required").max(200,"Title too long"),description:r.z.string().max(1e3,"Description too long").optional(),startTime:r.z.string().datetime("Invalid start time"),endTime:r.z.string().datetime("Invalid end time"),depositAmount:r.z.number().positive("Deposit must be positive").optional(),totalAmount:r.z.number().positive("Total amount must be positive").optional(),notes:r.z.string().max(1e3,"Notes too long").optional()}).refine(e=>new Date(e.endTime)>new Date(e.startTime),{message:"End time must be after start time",path:["endTime"]}),r.z.object({id:r.z.string().uuid(),artistId:r.z.string().uuid("Invalid artist ID").optional(),clientId:r.z.string().uuid("Invalid client ID").optional(),title:r.z.string().min(1,"Title is required").max(200,"Title too long").optional(),description:r.z.string().max(1e3,"Description too long").optional(),startTime:r.z.string().datetime("Invalid start time").optional(),endTime:r.z.string().datetime("Invalid end time").optional(),status:r.z.nativeEnum(n.Z).optional(),depositAmount:r.z.number().positive("Deposit must be positive").optional(),totalAmount:r.z.number().positive("Total amount must be positive").optional(),notes:r.z.string().max(1e3,"Notes too long").optional()}).refine(e=>!e.startTime||!e.endTime||new Date(e.endTime)>new Date(e.startTime),{message:"End time must be after start time",path:["endTime"]});let o=r.z.object({instagram:r.z.string().url("Invalid Instagram URL").optional(),facebook:r.z.string().url("Invalid Facebook URL").optional(),twitter:r.z.string().url("Invalid Twitter URL").optional(),tiktok:r.z.string().url("Invalid TikTok URL").optional()}),l=r.z.object({dayOfWeek:r.z.number().int().min(0).max(6),openTime:r.z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/,"Invalid time format (HH:mm)"),closeTime:r.z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/,"Invalid time format (HH:mm)"),isClosed:r.z.boolean().default(!1)});r.z.object({id:r.z.string().uuid(),studioName:r.z.string().min(1,"Studio name is required"),description:r.z.string().min(10,"Description must be at least 10 characters"),address:r.z.string().min(5,"Address is required"),phone:r.z.string().regex(/^[\+]?[1-9][\d]{0,15}$/,"Invalid phone number"),email:r.z.string().email("Invalid email address"),socialMedia:o,businessHours:r.z.array(l),heroImage:r.z.string().url("Invalid hero image URL").optional(),logoUrl:r.z.string().url("Invalid logo URL").optional()});let u=r.z.object({studioName:r.z.string().min(1,"Studio name is required").max(100,"Studio name too long").optional(),description:r.z.string().min(10,"Description must be at least 10 characters").max(1e3,"Description too long").optional(),address:r.z.string().min(5,"Address is required").max(200,"Address too long").optional(),phone:r.z.string().regex(/^[\+]?[1-9][\d]{0,15}$/,"Invalid phone number").optional(),email:r.z.string().email("Invalid email address").optional(),socialMedia:o.optional(),businessHours:r.z.array(l).optional(),heroImage:r.z.string().url("Invalid hero image URL").optional(),logoUrl:r.z.string().url("Invalid logo URL").optional()});r.z.object({id:r.z.string().uuid(),filename:r.z.string().min(1,"Filename is required"),originalName:r.z.string().min(1,"Original name is required"),mimeType:r.z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9!#$&\-\^_]*\/[a-zA-Z0-9][a-zA-Z0-9!#$&\-\^_.]*$/,"Invalid MIME type"),size:r.z.number().positive("File size must be positive"),url:r.z.string().url("Invalid file URL"),uploadedBy:r.z.string().uuid("Invalid user ID")}),r.z.object({filename:r.z.string().min(1,"Filename is required"),originalName:r.z.string().min(1,"Original name is required"),mimeType:r.z.string().regex(/^image\/(jpeg|jpg|png|gif|webp)$/,"Only image files are allowed"),size:r.z.number().positive("File size must be positive").max(10485760,"File too large (max 10MB)"),uploadedBy:r.z.string().uuid("Invalid user ID")});let d=r.z.object({page:r.z.string().nullable().transform(e=>e||"1").pipe(r.z.string().regex(/^\d+$/).transform(Number).pipe(r.z.number().int().min(1))),limit:r.z.string().nullable().transform(e=>e||"10").pipe(r.z.string().regex(/^\d+$/).transform(Number).pipe(r.z.number().int().min(1).max(100)))}),m=r.z.object({isActive:r.z.string().nullable().transform(e=>"true"===e||"false"!==e&&void 0).optional(),specialty:r.z.string().nullable().optional(),search:r.z.string().nullable().optional()});r.z.object({artistId:r.z.string().nullable().refine(e=>!e||r.z.string().uuid().safeParse(e).success,"Invalid artist ID").optional(),clientId:r.z.string().nullable().refine(e=>!e||r.z.string().uuid().safeParse(e).success,"Invalid client ID").optional(),status:r.z.string().nullable().refine(e=>!e||Object.values(n.Z).includes(e),"Invalid status").optional(),startDate:r.z.string().nullable().refine(e=>!e||r.z.string().datetime().safeParse(e).success,"Invalid start date").optional(),endDate:r.z.string().nullable().refine(e=>!e||r.z.string().datetime().safeParse(e).success,"Invalid end date").optional()}),r.z.object({email:r.z.string().email("Invalid email address"),password:r.z.string().min(1,"Password is required")}),r.z.object({name:r.z.string().min(1,"Name is required").max(100,"Name too long"),email:r.z.string().email("Invalid email address"),password:r.z.string().min(8,"Password must be at least 8 characters"),confirmPassword:r.z.string().min(1,"Please confirm your password")}).refine(e=>e.password===e.confirmPassword,{message:"Passwords don't match",path:["confirmPassword"]}),r.z.object({name:r.z.string().min(1,"Name is required").max(100,"Name too long"),email:r.z.string().email("Invalid email address"),phone:r.z.string().regex(/^[\+]?[1-9][\d]{0,15}$/,"Invalid phone number").optional(),subject:r.z.string().min(1,"Subject is required").max(200,"Subject too long"),message:r.z.string().min(10,"Message must be at least 10 characters").max(1e3,"Message too long")}),r.z.object({artistId:r.z.string().uuid("Please select an artist"),name:r.z.string().min(1,"Name is required").max(100,"Name too long"),email:r.z.string().email("Invalid email address"),phone:r.z.string().regex(/^[\+]?[1-9][\d]{0,15}$/,"Invalid phone number"),preferredDate:r.z.string().min(1,"Please select a preferred date"),tattooDescription:r.z.string().min(10,"Please provide more details about your tattoo").max(1e3,"Description too long"),size:r.z.enum(["small","medium","large","sleeve"],{required_error:"Please select a size"}),placement:r.z.string().min(1,"Please specify placement").max(100,"Placement description too long"),budget:r.z.string().optional(),hasAllergies:r.z.boolean().default(!1),allergies:r.z.string().max(500,"Allergies description too long").optional(),additionalNotes:r.z.string().max(500,"Additional notes too long").optional()})}};var t=require("../../../webpack-runtime.js");t.C(e);var i=e=>t(t.s=e),r=t.X(0,[9379,8213,4833,1253],()=>i(60349));module.exports=r})();