@@ -18,7 +18,13 @@ import {
1818 createFollowSubscription ,
1919 ingestFederationInbox ,
2020 listFederationIssues ,
21- listFollowSubscriptions
21+ listFollowSubscriptions ,
22+ makeFederationActorDocument ,
23+ makeFederationContext ,
24+ makeFederationFollowersCollection ,
25+ makeFederationFollowingCollection ,
26+ makeFederationLikedCollection ,
27+ makeFederationOutboxCollection
2228} from "./services/federation.js"
2329import {
2430 createProjectFromRequest ,
@@ -117,6 +123,55 @@ const readCreateProjectRequest = () => HttpServerRequest.schemaBodyJson(CreatePr
117123const readCreateFollowRequest = ( ) => HttpServerRequest . schemaBodyJson ( CreateFollowRequestSchema )
118124const readInboxPayload = ( ) => HttpServerRequest . schemaBodyJson ( Schema . Unknown )
119125
126+ const configuredFederationPublicOrigin =
127+ process . env [ "DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN" ] ??
128+ process . env [ "DOCKER_GIT_API_PUBLIC_URL" ]
129+
130+ const configuredFederationActorUsername =
131+ process . env [ "DOCKER_GIT_FEDERATION_ACTOR" ] ?? "docker-git"
132+
133+ const readHeader = (
134+ request : HttpServerRequest . HttpServerRequest ,
135+ key : string
136+ ) : string | undefined => {
137+ const value = request . headers [ key . toLowerCase ( ) ]
138+ return typeof value === "string" && value . trim ( ) . length > 0 ? value . trim ( ) : undefined
139+ }
140+
141+ const firstCommaValue = ( value : string | undefined ) : string | undefined => {
142+ if ( value === undefined ) {
143+ return undefined
144+ }
145+ const first = value . split ( "," ) [ 0 ] ?. trim ( )
146+ return first && first . length > 0 ? first : undefined
147+ }
148+
149+ const resolveRequestOrigin = ( request : HttpServerRequest . HttpServerRequest ) : string => {
150+ const forwardedHost = firstCommaValue ( readHeader ( request , "x-forwarded-host" ) )
151+ const host = forwardedHost ?? readHeader ( request , "host" )
152+ const proto = firstCommaValue ( readHeader ( request , "x-forwarded-proto" ) ) ?? "http"
153+ if ( host === undefined || host . length === 0 ) {
154+ return "http://localhost:3334"
155+ }
156+ return `${ proto } ://${ host } `
157+ }
158+
159+ const resolveFederationContext = (
160+ request : HttpServerRequest . HttpServerRequest ,
161+ requestedDomain ?: string | undefined
162+ ) => {
163+ const fromBody = requestedDomain ?. trim ( )
164+ const publicOrigin =
165+ fromBody && fromBody . length > 0
166+ ? fromBody
167+ : configuredFederationPublicOrigin ?? resolveRequestOrigin ( request )
168+
169+ return makeFederationContext ( {
170+ publicOrigin,
171+ actorUsername : configuredFederationActorUsername
172+ } )
173+ }
174+
120175export const makeRouter = ( ) => {
121176 const base = HttpRouter . empty . pipe (
122177 HttpRouter . get ( "/" , textResponse ( uiHtml , "text/html; charset=utf-8" , 200 ) ) ,
@@ -130,11 +185,53 @@ export const makeRouter = () => {
130185 Effect . catchAll ( errorResponse )
131186 )
132187 ) ,
188+ HttpRouter . get (
189+ "/v1/federation/actor" ,
190+ Effect . gen ( function * ( _ ) {
191+ const request = yield * _ ( HttpServerRequest . HttpServerRequest )
192+ const context = yield * _ ( resolveFederationContext ( request ) )
193+ return yield * _ ( jsonResponse ( makeFederationActorDocument ( context ) , 200 ) )
194+ } ) . pipe ( Effect . catchAll ( errorResponse ) )
195+ ) ,
196+ HttpRouter . get (
197+ "/v1/federation/outbox" ,
198+ Effect . gen ( function * ( _ ) {
199+ const request = yield * _ ( HttpServerRequest . HttpServerRequest )
200+ const context = yield * _ ( resolveFederationContext ( request ) )
201+ return yield * _ ( jsonResponse ( makeFederationOutboxCollection ( context ) , 200 ) )
202+ } ) . pipe ( Effect . catchAll ( errorResponse ) )
203+ ) ,
204+ HttpRouter . get (
205+ "/v1/federation/followers" ,
206+ Effect . gen ( function * ( _ ) {
207+ const request = yield * _ ( HttpServerRequest . HttpServerRequest )
208+ const context = yield * _ ( resolveFederationContext ( request ) )
209+ return yield * _ ( jsonResponse ( makeFederationFollowersCollection ( context ) , 200 ) )
210+ } ) . pipe ( Effect . catchAll ( errorResponse ) )
211+ ) ,
212+ HttpRouter . get (
213+ "/v1/federation/following" ,
214+ Effect . gen ( function * ( _ ) {
215+ const request = yield * _ ( HttpServerRequest . HttpServerRequest )
216+ const context = yield * _ ( resolveFederationContext ( request ) )
217+ return yield * _ ( jsonResponse ( makeFederationFollowingCollection ( context ) , 200 ) )
218+ } ) . pipe ( Effect . catchAll ( errorResponse ) )
219+ ) ,
220+ HttpRouter . get (
221+ "/v1/federation/liked" ,
222+ Effect . gen ( function * ( _ ) {
223+ const request = yield * _ ( HttpServerRequest . HttpServerRequest )
224+ const context = yield * _ ( resolveFederationContext ( request ) )
225+ return yield * _ ( jsonResponse ( makeFederationLikedCollection ( context ) , 200 ) )
226+ } ) . pipe ( Effect . catchAll ( errorResponse ) )
227+ ) ,
133228 HttpRouter . post (
134229 "/v1/federation/follows" ,
135230 Effect . gen ( function * ( _ ) {
136- const request = yield * _ ( readCreateFollowRequest ( ) )
137- const created = yield * _ ( createFollowSubscription ( request ) )
231+ const requestBody = yield * _ ( readCreateFollowRequest ( ) )
232+ const request = yield * _ ( HttpServerRequest . HttpServerRequest )
233+ const context = yield * _ ( resolveFederationContext ( request , requestBody . domain ) )
234+ const created = yield * _ ( createFollowSubscription ( requestBody , context ) )
138235 return yield * _ ( jsonResponse ( created , 201 ) )
139236 } ) . pipe ( Effect . catchAll ( errorResponse ) )
140237 ) ,
@@ -152,7 +249,10 @@ export const makeRouter = () => {
152249 const result = yield * _ ( ingestFederationInbox ( payload ) )
153250 return yield * _ ( jsonResponse ( { result } , 202 ) )
154251 } ) . pipe ( Effect . catchAll ( errorResponse ) )
155- ) ,
252+ )
253+ )
254+
255+ const withProjects = base . pipe (
156256 HttpRouter . get (
157257 "/v1/projects" ,
158258 listProjects ( ) . pipe (
@@ -226,7 +326,7 @@ export const makeRouter = () => {
226326 )
227327 )
228328
229- const withAgents = base . pipe (
329+ const withAgents = withProjects . pipe (
230330 HttpRouter . post (
231331 "/v1/projects/:projectId/agents" ,
232332 Effect . gen ( function * ( _ ) {
0 commit comments