@@ -3,25 +3,24 @@ use std::sync::Arc;
3
3
use opentelemetry:: global;
4
4
use poem:: middleware:: OpenTelemetryMetrics ;
5
5
use poem:: web:: Data ;
6
- use poem:: Response ;
7
- use poem:: {
8
- endpoint:: StaticFilesEndpoint , get, handler, listener:: TcpListener , middleware:: Cors ,
9
- web:: Html , EndpointExt , Route , Server ,
10
- } ;
11
- use poem_openapi:: { OpenApi , OpenApiService , Tags } ;
12
- use serde_json:: { self , Value } ;
6
+ use poem:: { Body , EndpointExt , IntoResponse , Request , Response } ;
7
+ use poem:: { get, handler, listener:: TcpListener , middleware:: Cors , Route , Server } ;
8
+ use reqwest:: StatusCode ;
13
9
use tracing:: info;
10
+ use futures:: StreamExt ;
14
11
15
12
use crate :: middlewares:: tracing:: TraceId ;
13
+ use crate :: models:: deployment:: { Deployment , DeploymentFile } ;
14
+ use crate :: models:: domain:: Domain ;
15
+ use crate :: models:: site:: Site ;
16
+ use crate :: routes:: error:: HttpError ;
16
17
use crate :: state:: State ;
17
18
18
19
pub async fn serve ( state : State ) {
19
20
info ! ( "Serving Router" ) ;
20
21
21
22
let app = Route :: new ( )
22
- // .nest("/api", api_service)
23
- // .nest("/", file_endpoint)
24
- . at ( "/" , get ( not_found) )
23
+ . at ( "*" , get ( resolve_http) )
25
24
. with ( Cors :: new ( ) )
26
25
. with ( TraceId :: new ( Arc :: new ( global:: tracer ( "edgeserver" ) ) ) )
27
26
. with ( OpenTelemetryMetrics :: new ( ) )
@@ -33,7 +32,88 @@ pub async fn serve(state: State) {
33
32
}
34
33
35
34
#[ handler]
36
- async fn not_found ( ) -> Html < & ' static str > {
35
+ async fn resolve_http ( request : & Request , state : Data < & State > ) -> impl IntoResponse {
36
+ let headers = request. headers ( ) ;
37
+ let host = match headers. get ( "host" ) {
38
+ Some ( host) => host. to_str ( ) . unwrap_or ( "localhost" ) ,
39
+ None => "localhost" ,
40
+ } ;
41
+ let path = request. uri ( ) . path ( ) ;
42
+ let path = path. trim_start_matches ( '/' ) ;
43
+
44
+ info ! ( "Router request at: {} {}" , host, path) ;
45
+
46
+ let deployment = get_last_deployment ( host, & state. clone ( ) ) . await ;
47
+
48
+ if let Ok ( deployment) = deployment {
49
+ info ! ( "Deployment found: {}" , deployment. deployment_id) ;
50
+
51
+ let path = if path. is_empty ( ) {
52
+ "index.html"
53
+ } else {
54
+ path
55
+ } ;
56
+
57
+ let file = DeploymentFile :: get_file_by_path ( & state. clone ( ) . database , & deployment. deployment_id , path)
58
+ . await . ok ( ) ;
59
+
60
+ if let Some ( deployment_file) = file {
61
+ info ! ( "File found: {}" , deployment_file. file_hash) ;
62
+
63
+ // stream file from s3 storage and return it
64
+ let s3_path = deployment_file. file_hash . clone ( ) ;
65
+ if let Ok ( s3_data) = state. storage . bucket . get_object_stream ( s3_path) . await {
66
+ // Stream the S3 response bytes directly to the client
67
+ let stream = s3_data. bytes . map ( |chunk| {
68
+ chunk. map_err ( |e| {
69
+ info ! ( "Error streaming file: {}" , e) ;
70
+ std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e)
71
+ } )
72
+ } ) ;
73
+
74
+ let body = Body :: from_bytes_stream ( stream) ;
75
+ return Response :: builder ( )
76
+ . status ( StatusCode :: OK )
77
+ . header ( "content-type" , deployment_file. deployment_file_mime_type . clone ( ) )
78
+ . body ( body) ;
79
+ } else {
80
+ info ! ( "File not found in s3" ) ;
81
+ }
82
+ } else {
83
+ info ! ( "No file found" ) ;
84
+ }
85
+ } else {
86
+ info ! ( "No deployment found" ) ;
87
+ }
88
+
37
89
// inline 404 template
38
- Html ( include_str ! ( "./404.html" ) )
90
+ Response :: builder ( )
91
+ . status ( StatusCode :: NOT_FOUND )
92
+ . body ( Body :: from_string ( include_str ! ( "./404.html" ) . to_string ( ) ) )
93
+ }
94
+
95
+ async fn get_last_deployment ( host : & str , state : & State ) -> Result < Deployment , HttpError > {
96
+ // get domain
97
+ // get site
98
+ // get last deployment
99
+ // get file at path in deployment
100
+ // otherwise return default /index.html if exists
101
+ let domain = Domain :: existing_domain_by_name ( host, & state. clone ( ) )
102
+ . await . ok ( ) . flatten ( ) ;
103
+
104
+ if let Some ( domain) = domain {
105
+ let site = Site :: get_by_id ( & state. clone ( ) . database , & domain. site_id )
106
+ . await . ok ( ) ;
107
+
108
+ if let Some ( site) = site {
109
+ let deployment = Deployment :: get_last_by_site_id ( & state. clone ( ) . database , & site. site_id )
110
+ . await . ok ( ) ;
111
+
112
+ if let Some ( deployment) = deployment {
113
+ return Ok ( deployment) ;
114
+ }
115
+ }
116
+ }
117
+
118
+ Err ( HttpError :: NotFound )
39
119
}
0 commit comments