11import * as Alchemy from "alchemy" ;
22import * as Cloudflare from "alchemy/Cloudflare" ;
3+ import * as Output from "alchemy/Output" ;
4+ import * as Config from "effect/Config" ;
35import * as Effect from "effect/Effect" ;
6+ import * as Option from "effect/Option" ;
7+ import * as Redacted from "effect/Redacted" ;
48
59import { DatabaseHyperdrive } from "./infrastructure/Hyperdrive.ts" ;
610import { PaywallArtifactsBucket } from "./r2/PaywallArtifactsBucket.ts" ;
711import { PublicFileStorageBucket } from "./r2/PublicFileStorageBucket.ts" ;
812import { CommunityWebsite } from "./workers/WwwWorker.ts" ;
913import CommunityBackend from "./workers/BackendWorker.ts" ;
14+ import MimicDbWorker , { MIMIC_DB_DEV_PORT } from "./workers/MimicDbWorker.ts" ;
15+
16+ const dieOnBlankWwwOrigin = ( ) : never =>
17+ Effect . runSync (
18+ Effect . die (
19+ new Error ( "www origin resolved empty — refusing to bind a blank CORS_ORIGINS on mimic-db" ) ,
20+ ) ,
21+ ) ;
1022
1123/** Resolved Community Cloudflare deployment outputs. */
1224export interface CommunityStackOutput {
1325 readonly backendUrl : string ;
1426 readonly hyperdriveId : string ;
27+ readonly mimicDbUrl : string ;
1528 readonly wwwUrl : string ;
1629}
1730
@@ -22,15 +35,101 @@ export default Alchemy.Stack(
2235 state : Cloudflare . state ( ) ,
2336 } ,
2437 Effect . gen ( function * ( ) {
38+ const { stage } = yield * Alchemy . Stack ;
39+ const dev = Option . match ( yield * Effect . serviceOption ( Alchemy . AlchemyContext ) , {
40+ onNone : ( ) => false ,
41+ onSome : ( context ) => context . dev ,
42+ } ) ;
43+ const isEphemeral = stage !== "production" && stage !== "preview" ;
2544 const hyperdrive = yield * DatabaseHyperdrive ;
2645 yield * PaywallArtifactsBucket ;
2746 yield * PublicFileStorageBucket ;
2847 const backend = yield * CommunityBackend ;
48+ const mimicDb = yield * MimicDbWorker ;
49+ yield * backend . bind `MimicHostServiceBinding` ( {
50+ bindings : [
51+ {
52+ type : "service" ,
53+ name : "MIMIC_HOST" ,
54+ service : mimicDb . workerName ,
55+ } ,
56+ ] ,
57+ } ) ;
2958 const www = yield * CommunityWebsite ( { apiUrl : backend . url . as < string > ( ) } ) ;
3059
60+ if ( ! dev ) {
61+ yield * mimicDb . bind `MimicPublicBaseUrl` ( {
62+ bindings : [
63+ {
64+ type : "plain_text" ,
65+ name : "MIMIC_PUBLIC_BASE_URL" ,
66+ text : Output . map ( mimicDb . url , ( url ) => url ?? "" ) ,
67+ } ,
68+ ] ,
69+ } ) ;
70+ }
71+
72+ const mimicRootPassword = yield * Config . redacted ( "MIMIC_ROOT_PASSWORD" ) . pipe (
73+ Config . withDefault ( Redacted . make ( "" ) ) ,
74+ Effect . flatMap ( ( password ) => {
75+ if ( Redacted . value ( password ) !== "" ) return Effect . succeed ( password ) ;
76+ if ( isEphemeral ) return Effect . succeed ( Redacted . make ( "password" ) ) ;
77+ return Effect . die (
78+ new Error ( "MIMIC_ROOT_PASSWORD must be set for production/preview deploys" ) ,
79+ ) ;
80+ } ) ,
81+ ) ;
82+ const resolveMimicCorsOrigins = ( ) => {
83+ if ( dev ) {
84+ return [
85+ "http://localhost:3000" ,
86+ "http://localhost:5173" ,
87+ "http://localhost:4173" ,
88+ "http://localhost:4460" ,
89+ "http://localhost:3003" ,
90+ ] . join ( "," ) ;
91+ }
92+ return Output . map ( www . url , ( url ) => {
93+ const origin = ( url ?? "" ) . replace ( / \/ + $ / , "" ) ;
94+ if ( origin === "" && ! isEphemeral ) return dieOnBlankWwwOrigin ( ) ;
95+ return origin ;
96+ } ) ;
97+ } ;
98+ const mimicCorsOrigins = resolveMimicCorsOrigins ( ) ;
99+ const resolveMimicDbUrl = ( ) => {
100+ if ( dev ) return `http://localhost:${ MIMIC_DB_DEV_PORT } ` ;
101+ return Output . map ( mimicDb . url , ( url ) => url ?? "" ) ;
102+ } ;
103+
104+ yield * mimicDb . bind `MimicDbSecurity` ( {
105+ bindings : [
106+ {
107+ type : "secret_text" ,
108+ name : "ROOT_PASSWORD" ,
109+ text : Redacted . value ( mimicRootPassword ) ,
110+ } ,
111+ { type : "plain_text" , name : "CORS_ORIGINS" , text : mimicCorsOrigins } ,
112+ ] ,
113+ } ) ;
114+ yield * backend . bind `MimicDbCredentials` ( {
115+ bindings : [
116+ {
117+ type : "plain_text" ,
118+ name : "MIMIC_DB_URL" ,
119+ text : resolveMimicDbUrl ( ) ,
120+ } ,
121+ {
122+ type : "secret_text" ,
123+ name : "MIMIC_ROOT_PASSWORD" ,
124+ text : Redacted . value ( mimicRootPassword ) ,
125+ } ,
126+ ] ,
127+ } ) ;
128+
31129 return {
32130 backendUrl : backend . url ,
33131 hyperdriveId : hyperdrive . hyperdriveId ,
132+ mimicDbUrl : mimicDb . url ,
34133 wwwUrl : www . url ,
35134 } ;
36135 } ) ,
0 commit comments