Example #1
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseCors(builder => builder
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowAnyOrigin()
                        .WithExposedHeaders(CorsHelper.GetExposedHeaders()));

            app.UseSimpleExceptionHandler();

            // httpContext parameter can be used to create a tus configuration based on current user, domain, host, port or whatever.
            // In this case we just return the same configuration for everyone.
            app.UseTus(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>()));

            // All GET requests to tusdotnet are forwarded so that you can handle file downloads.
            // This is done because the file's metadata is domain specific and thus cannot be handled
            // in a generic way by tusdotnet.
            app.UseSimpleDownloadMiddleware(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>()));
        }
Example #2
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.Use((context, next) =>
            {
                // Default limit was changed some time ago. Should work by setting MaxRequestBodySize to null using ConfigureKestrel but this does not seem to work for IISExpress.
                // Source: https://github.com/aspnet/Announcements/issues/267
                context.Features.Get <IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = null;
                return(next.Invoke());
            });
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseCors(builder => builder
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowAnyOrigin()
                        .WithExposedHeaders(CorsHelper.GetExposedHeaders()));

            //app.UseSimpleExceptionHandler();

            // httpContext parameter can be used to create a tus configuration based on current user, domain, host, port or whatever.
            // In this case we just return the same configuration for everyone.
            app.UseTus(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>()));

            // All GET requests to tusdotnet are forwarded so that you can handle file downloads.
            // This is done because the file's metadata is domain specific and thus cannot be handled
            // in a generic way by tusdotnet.
            //app.UseSimpleDownloadMiddleware(httpContext => Task.FromResult(httpContext.RequestServices.GetService<DefaultTusConfiguration>()));
        }
Example #3
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseCors(builder => builder
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowAnyOrigin()
                        .WithExposedHeaders(CorsHelper.GetExposedHeaders()));

            app.UseSimpleExceptionHandler();

            // httpContext parameter can be used to create a tus configuration based on current user, domain, host, port or whatever.
            // In this case we just return the same configuration for everyone.
            app.UseTus(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>()));

            // All GET requests to tusdotnet are forwarded so that you can handle file downloads.
            // This is done because the file's metadata is domain specific and thus cannot be handled
            // in a generic way by tusdotnet.
            app.UseSimpleDownloadMiddleware(httpContext => Task.FromResult(httpContext.RequestServices.GetService <DefaultTusConfiguration>()));

            // Start cleanup job to remove incomplete expired files.
            var cleanupService = app.ApplicationServices.GetService <ExpiredFilesCleanupService>();

            cleanupService.Start();
        }
Example #4
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            // Identity
            services.AddDbContext <SecurityContext>(options =>
                                                    options.UseNpgsql(Configuration.GetConnectionString("Default"),
                                                                      x => x.MigrationsHistoryTable("__MigrationsHistory", SecurityContext.DEFAULT_SCHEMA)));

            IdentityHelper.ConfigureService(services);

            // Helpers
            AuthenticationHelper.ConfigureService(services, Configuration["JwtSecurityToken:Issuer"], Configuration["JwtSecurityToken:Audience"], Configuration["JwtSecurityToken:Key"]);
            CorsHelper.ConfigureService(services);
            SwaggerHelper.ConfigureService(services);

            // Settings
            services.Configure <EmailSettings>(Configuration.GetSection("Email"));
            services.Configure <ClientAppSettings>(Configuration.GetSection("ClientApp"));
            services.Configure <JwtSecurityTokenSettings>(Configuration.GetSection("JwtSecurityToken"));
            services.Configure <QRCodeSettings>(Configuration.GetSection("QRCode"));

            // Services
            services.AddTransient <IEmailService, EmailService>();

            // Azure
            // Azure Storage Services
            services.AddScoped <IBlobStorage>(s => new BlobStorage(Configuration["ConnectionStrings:AzureStorage"], Configuration["AzureStorage:ContainerName"], Configuration["AzureStorage:Url"]));
            services.AddScoped <IQueueStorage>(s => new QueueStorage(Configuration["ConnectionStrings:AzureStorage"]));

            // Data
        }
 public static IAppBuilder UseParticipantLibraryCore(this IAppBuilder app, IAppSettings appSettings, IAmAParticipantLibrary library)
 {
     app.UseStageMarker(PipelineStage.MapHandler);
     app.UseWebApi(WebApiConfig.Configure(appSettings, library));
     CorsHelper.Register(app);
     ConfigureSignalR(app, appSettings);
     return(app);
 }
Example #6
0
        public static void Render(FrontContext context)
        {
            var start = System.DateTime.Now;

            var code = context.SiteDb.Code.Get(context.Route.objectId);

            if (code != null)
            {
                string result     = string.Empty;
                var    enableCORS = context?.WebSite?.EnableCORS ?? false;

                if (code.IsJson)
                {
                    result = code.Body;
                }
                else if (CorsHelper.IsOptionsRequest(context) && enableCORS)
                {
                    result = "";
                }
                else
                {
                    result = Scripting.Manager.ExecuteCode(context.RenderContext, code.Body, code.Id);
                }

                //context.RenderContext.Response.ContentType = "application/javascript";

                if (code.Cors || enableCORS)
                {
                    CorsHelper.HandleHeaders(context);
                }

                if (!string.IsNullOrEmpty(result))
                {
                    context.RenderContext.Response.Body = Encoding.UTF8.GetBytes(result);
                }
            }
            else
            {
                context.RenderContext.Response.StatusCode = 404;
            }

            if (context.WebSite.EnableVisitorLog)
            {
                string detail = "";
                if (context.RenderContext.Request.Body != null)
                {
                    detail = context.RenderContext.Request.Body;
                }

                context.Log.AddEntry("API call", context.RenderContext.Request.RawRelativeUrl, start, System.DateTime.Now, (short)context.RenderContext.Response.StatusCode, detail);

                context.Page = new Models.Page()
                {
                    Name = "system api page"
                };
            }
        }
Example #7
0
        public static IServiceCollection AddCorsPolicy(
            this IServiceCollection services,
            string corsPolicy, string apiUrl)
        {
            var allowedOrigins = CorsHelper.GetAllowedOrigins(apiUrl);

            if (allowedOrigins != null)
            {
                services.AddCors(x => x.AddPolicy(corsPolicy,
                                                  builder =>
                                                  builder.WithOrigins(allowedOrigins).AllowAnyHeader().AllowAnyMethod()));
            }
            services.AddTransient <ICorsPolicyAccessor, CorsPolicyAccessor>();

            return(services);
        }
        public async Task InvokeAsync(HttpContext context, ICorsPolicyAccessor accessor

                                      )
        {
            var policy = accessor.GetPolicy(_policyName);

            string[] origins = CorsHelper.GetAllowedOrigins(_apiUrl);
            if (origins?.Length > 0)
            {
                var policyOrigins = policy.Origins;
                var newOrigins    = origins.Except(policyOrigins);
                foreach (var origin in newOrigins)
                {
                    policy.Origins.Add(origin);
                }
            }

            await _next(context);
        }
Example #9
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //https://github.com/shammelburg/CoreIdentity
            // Identity
            services.AddDbContext <Api.Identity.SecurityContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
            // Tools->NuGet Package Manager -> Package Manager Console
            // Initialise
            // add-migration init -Context SecurityContext
            // update or create DB
            // update-database -Context SecurityContext
            IdentityHelper.ConfigureService(services);

            // Helpers
            AuthenticationHelper.ConfigureService(services, Configuration["JwtSecurityToken:Issuer"], Configuration["JwtSecurityToken:Audience"], Configuration["JwtSecurityToken:Key"]);
            CorsHelper.ConfigureService(services);
            SwaggerHelper.ConfigureService(services);

            // Settings
            services.Configure <EmailSettings>(Configuration.GetSection("Email"));
            services.Configure <ClientAppSettings>(Configuration.GetSection("ClientApp"));
            services.Configure <JwtSecurityTokenSettings>(Configuration.GetSection("JwtSecurityToken"));
            //services.Configure<QRCodeSettings>(Configuration.GetSection("QRCode"));

            // Services
            services.AddTransient <IEmailService, EmailService>();
            services.AddScoped(typeof(IGenericRepository <>), typeof(GenericRepository <>));
            services.AddTransient <IUnitOfWork, UnitOfWork>();

            services.AddControllers();
            services.AddHttpContextAccessor();

            // Mapper
            services.AddAutoMapper(typeof(AutoMapping));

            //Define upload
            services.Configure <FormOptions>(o => {
                o.ValueLengthLimit         = int.MaxValue;
                o.MultipartBodyLengthLimit = int.MaxValue;
                o.MemoryBufferThreshold    = int.MaxValue;
            });
        }
Example #10
0
        private void SetupCorsPolicy(IAppBuilder app)
        {
            var corsPolicy = new System.Web.Cors.CorsPolicy
            {
                AllowAnyHeader = true,
                AllowAnyMethod = true,
                AllowAnyOrigin = true
            };

            corsPolicy.GetType()
            .GetProperty(nameof(corsPolicy.ExposedHeaders))
            .SetValue(corsPolicy, CorsHelper.GetExposedHeaders());

            app.UseCors(new CorsOptions
            {
                PolicyProvider = new CorsPolicyProvider
                {
                    PolicyResolver = context => Task.FromResult(corsPolicy)
                }
            });
        }
Example #11
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            // Register the Swagger generator, defining 1 or more Swagger documents
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo {
                    Title = "XamWalletWebApi", Version = "v1"
                });
            });

            services.AddDbContext <AuthenticationContext>(options =>
                                                          options.UseSqlServer(Configuration.GetConnectionString("ProductionConnection")));

            services.AddIdentity <ApplicationUser, IdentityRole>(
                options =>
            {
                options.Lockout.AllowedForNewUsers      = true;
                options.Lockout.MaxFailedAccessAttempts = 10;
            })
            .AddEntityFrameworkStores <AuthenticationContext>()
            .AddDefaultTokenProviders();


            services.Configure <IdentityOptions>(options =>
            {
                options.Password.RequireDigit           = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireLowercase       = false;
                options.Password.RequireUppercase       = false;
                options.Password.RequiredLength         = 4;
            }
                                                 );

            AuthenticationHelper.ConfigureService(services, Configuration["JwtSecurityToken:Issuer"], Configuration["JwtSecurityToken:Audience"], Configuration["JwtSecurityToken:Key"]);
            CorsHelper.ConfigureService(services);
        }
Example #12
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Identity
            services.AddDbContext <SecurityContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:Default"]));

            // Tools->NuGet Package Manager -> Package Manager Console
            // Initialise
            // add-migration init -Context SecurityContext
            // update or create DB
            // update-database -Context SecurityContext
            IdentityHelper.ConfigureService(services);

            // Helpers
            AuthenticationHelper.ConfigureService(services, Configuration["JwtSecurityToken:Issuer"], Configuration["JwtSecurityToken:Audience"], Configuration["JwtSecurityToken:Key"]);
            CorsHelper.ConfigureService(services);
            SwaggerHelper.ConfigureService(services);

            // Settings
            services.Configure <EmailSettings>(Configuration.GetSection("Email"));
            services.Configure <ClientAppSettings>(Configuration.GetSection("ClientApp"));
            services.Configure <JwtSecurityTokenSettings>(Configuration.GetSection("JwtSecurityToken"));
            services.Configure <QRCodeSettings>(Configuration.GetSection("QRCode"));

            // Services
            services.AddTransient <IEmailService, EmailService>();

            // Azure
            // Azure Storage Services
            services.AddScoped <IBlobStorage>(s => new BlobStorage(Configuration["ConnectionStrings:AzureStorage"], Configuration["AzureStorage:ContainerName"], Configuration["AzureStorage:Url"]));
            services.AddScoped <IQueueStorage>(s => new QueueStorage(Configuration["ConnectionStrings:AzureStorage"]));

            // Data
            services.AddDbContextPool <DataContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:Default"]));
            services.AddScoped <IExampleRepo, ExampleRepo>();

            services.AddControllers();
        }
Example #13
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Auto Mapper Configurations
            var mappingConfig = new MapperConfiguration(mc =>
            {
                mc.AddProfile(new MappingProfile());
            });

            IMapper mapper = mappingConfig.CreateMapper();

            services.AddSingleton(mapper);

            // Identity
            services.AddDbContext <SchoolDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

            // Identity kurulumu için
            // Tools->NuGet Package Manager -> Package Manager Console
            // add-migration init -Context SecurityContext
            // update-database -Context SecurityContext
            IdentityHelper.ConfigureService(services);

            // Helpers
            AuthenticationHelper.ConfigureService(services, Configuration["JwtSecurityToken:Issuer"], Configuration["JwtSecurityToken:Audience"], Configuration["JwtSecurityToken:Key"]);
            CorsHelper.ConfigureService(services);

            // Settings
            services.Configure <JwtSecurityTokenSettings>(Configuration.GetSection("JwtSecurityToken"));

            // Services
            //services.AddTransient<IEmailService, EmailService>();

            //Data
            services.AddDbContext <SchoolDbContext>(options =>
                                                    options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title   = "School API",
                    Version = "v1"
                });
                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    In          = ParameterLocation.Header,
                    Description = "Please insert JWT with Bearer into field",
                    Name        = "Authorization",
                    Type        = SecuritySchemeType.ApiKey
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id   = "Bearer"
                            }
                        },
                        new string[] { }
                    }
                });
            });

            services.AddControllers()
            .AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
        }
Example #14
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
                              IApplicationLifetime applicationLifetime)
        {
            loggerFactory.AddConsole();

            var logger = loggerFactory.CreateLogger <Startup>();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseCors(builder => builder
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowAnyOrigin()
                        .WithExposedHeaders(CorsHelper.GetExposedHeaders()));

            app.Use(async(context, next) =>
            {
                try
                {
                    await next.Invoke();
                }
                catch (Exception exc)
                {
                    logger.LogError(null, exc, exc.Message);
                    context.Response.StatusCode = 500;
                    await context.Response.WriteAsync("An internal server error has occurred", context.RequestAborted);
                }
            });

            app.UseTus(context => new DefaultTusConfiguration
            {
                UrlPath = "/files",
                Store   = new TusDiskStore(@"C:\tusfiles\"),
                Events  = new Events
                {
                    OnBeforeCreateAsync = ctx =>
                    {
                        // Partial files are not complete so we do not need to validate
                        // the metadata in our example.
                        if (ctx.FileConcatenation is FileConcatPartial)
                        {
                            return(Task.CompletedTask);
                        }

                        if (!ctx.Metadata.ContainsKey("name"))
                        {
                            ctx.FailRequest("name metadata must be specified. ");
                        }

                        if (!ctx.Metadata.ContainsKey("contentType"))
                        {
                            ctx.FailRequest("contentType metadata must be specified. ");
                        }

                        return(Task.CompletedTask);
                    },
                    OnCreateCompleteAsync = ctx =>
                    {
                        logger.LogInformation($"Created file {ctx.FileId} using {ctx.Store.GetType().FullName}");
                        return(Task.CompletedTask);
                    },
                    OnBeforeDeleteAsync = ctx =>
                    {
                        // Can the file be deleted? If not call ctx.FailRequest(<message>);
                        return(Task.CompletedTask);
                    },
                    OnDeleteCompleteAsync = ctx =>
                    {
                        logger.LogInformation($"Deleted file {ctx.FileId} using {ctx.Store.GetType().FullName}");
                        return(Task.CompletedTask);
                    },
                    OnFileCompleteAsync = ctx =>
                    {
                        logger.LogInformation($"Upload of {ctx.FileId} completed using {ctx.Store.GetType().FullName}");
                        // If the store implements ITusReadableStore one could access the completed file here.
                        // The default TusDiskStore implements this interface:
                        //var file = await ((tusdotnet.Interfaces.ITusReadableStore)ctx.Store).GetFileAsync(ctx.FileId, ctx.CancellationToken);
                        return(Task.CompletedTask);
                    }
                },
                // Set an expiration time where incomplete files can no longer be updated.
                // This value can either be absolute or sliding.
                // Absolute expiration will be saved per file on create
                // Sliding expiration will be saved per file on create and updated on each patch/update.
                Expiration = _absoluteExpiration
            });

            app.Use(async(context, next) =>
            {
                // All GET requests to tusdotnet are forwared so that you can handle file downloads.
                // This is done because the file's metadata is domain specific and thus cannot be handled
                // in a generic way by tusdotnet.
                if (!context.Request.Method.Equals("get", StringComparison.OrdinalIgnoreCase))
                {
                    await next.Invoke();
                    return;
                }

                var url = new Uri(context.Request.GetDisplayUrl());

                if (url.LocalPath.StartsWith("/files/", StringComparison.Ordinal))
                {
                    var fileId = url.LocalPath.Replace("/files/", "").Trim();
                    if (!string.IsNullOrEmpty(fileId))
                    {
                        var file = await _tusDiskStore.GetFileAsync(fileId, context.RequestAborted);

                        if (file == null)
                        {
                            context.Response.StatusCode = 404;
                            await context.Response.WriteAsync($"File with id {fileId} was not found.",
                                                              context.RequestAborted);
                            return;
                        }

                        var fileStream = await file.GetContentAsync(context.RequestAborted);
                        var metadata   = await file.GetMetadataAsync(context.RequestAborted);

                        context.Response.ContentType = metadata.ContainsKey("contentType")
                            ? metadata["contentType"].GetString(Encoding.UTF8)
                            : "application/octet-stream";

                        if (metadata.TryGetValue("name", out var nameMeta))
                        {
                            context.Response.Headers.Add("Content-Disposition",
                                                         new[] { $"attachment; filename=\"{nameMeta.GetString(Encoding.UTF8)}\"" });
                        }

                        await fileStream.CopyToAsync(context.Response.Body, 81920, context.RequestAborted);
                    }
                }
            });

            // Setup cleanup job to remove incomplete expired files.
            // This is just a simple example. In production one would use a cronjob/webjob and poll an endpoint that runs RemoveExpiredFilesAsync.
            var onAppDisposingToken = applicationLifetime.ApplicationStopping;

            Task.Run(async() =>
            {
                while (!onAppDisposingToken.IsCancellationRequested)
                {
                    logger.LogInformation("Running cleanup job...");
                    var numberOfRemovedFiles = await _tusDiskStore.RemoveExpiredFilesAsync(onAppDisposingToken);
                    logger.LogInformation(
                        $"Removed {numberOfRemovedFiles} expired files. Scheduled to run again in {_absoluteExpiration.Timeout.TotalMilliseconds} ms");
                    await Task.Delay(_absoluteExpiration.Timeout, onAppDisposingToken);
                }
            }, onAppDisposingToken);
        }
Example #15
0
        public void Configuration(IAppBuilder app)
        {
            var corsPolicy = new System.Web.Cors.CorsPolicy
            {
                AllowAnyHeader = true,
                AllowAnyMethod = true,
                AllowAnyOrigin = true
            };

            // ReSharper disable once PossibleNullReferenceException - nameof will cause compiler error if the property does not exist.
            corsPolicy.GetType()
            .GetProperty(nameof(corsPolicy.ExposedHeaders))
            .SetValue(corsPolicy, CorsHelper.GetExposedHeaders());

            app.UseCors(new CorsOptions
            {
                PolicyProvider = new CorsPolicyProvider
                {
                    PolicyResolver = context => Task.FromResult(corsPolicy)
                }
            });

            app.Use(async(context, next) =>
            {
                try
                {
                    await next.Invoke();
                }
                catch (Exception exc)
                {
                    Console.Error.WriteLine(exc);
                }
            });

            app.UseTus(request =>
            {
                return(new DefaultTusConfiguration
                {
                    Store = _tusDiskStore,
                    UrlPath = "/files",
                    OnUploadCompleteAsync = (fileId, store, cancellationToken) =>
                    {
                        Console.WriteLine(
                            $"Upload of {fileId} is complete. Callback also got a store of type {store.GetType().FullName}");
                        // If the store implements ITusReadableStore one could access the completed file here.
                        // The default TusDiskStore implements this interface:
                        // var file = await ((ITusReadableStore)store).GetFileAsync(fileId, cancellationToken);
                        return Task.FromResult(true);
                    },
                    // Set an expiration time where incomplete files can no longer be updated.
                    // This value can either be absolute or sliding.
                    // Absolute expiration will be saved per file on create
                    // Sliding expiration will be saved per file on create and updated on each patch/update.
                    Expiration = _absoluteExpiration
                });
            });

            app.Use(async(context, next) =>
            {
                // All GET requests to tusdotnet are forwared so that you can handle file downloads.
                // This is done because the file's metadata is domain specific and thus cannot be handled
                // in a generic way by tusdotnet.
                if (!context.Request.Method.Equals("get", StringComparison.InvariantCultureIgnoreCase))
                {
                    await next.Invoke();
                    return;
                }

                if (context.Request.Uri.LocalPath.StartsWith("/files/", StringComparison.Ordinal))
                {
                    var fileId = context.Request.Uri.LocalPath.Replace("/files/", "").Trim();
                    if (!string.IsNullOrEmpty(fileId))
                    {
                        var file = await _tusDiskStore.GetFileAsync(fileId, context.Request.CallCancelled);

                        if (file == null)
                        {
                            context.Response.StatusCode = 404;
                            await context.Response.WriteAsync($"File with id {fileId} was not found.", context.Request.CallCancelled);
                            return;
                        }

                        var fileStream = await file.GetContentAsync(context.Request.CallCancelled);
                        var metadata   = await file.GetMetadataAsync(context.Request.CallCancelled);

                        context.Response.ContentType = metadata.ContainsKey("contentType")
                                                        ? metadata["contentType"].GetString(Encoding.UTF8)
                                                        : "application/octet-stream";

                        if (metadata.ContainsKey("name"))
                        {
                            var name = metadata["name"].GetString(Encoding.UTF8);
                            context.Response.Headers.Add("Content-Disposition", new[] { $"attachment; filename=\"{name}\"" });
                        }

                        await fileStream.CopyToAsync(context.Response.Body, 81920, context.Request.CallCancelled);
                        return;
                    }
                }

                switch (context.Request.Uri.LocalPath)
                {
                case "/":
                    context.Response.ContentType = "text/html";
                    await context.Response.WriteAsync(File.ReadAllText("../../index.html"), context.Request.CallCancelled);
                    break;

                case "/tus.js":
                    context.Response.ContentType = "application/js";
                    await context.Response.WriteAsync(File.ReadAllText("../../tus.js"), context.Request.CallCancelled);
                    break;

                default:
                    context.Response.StatusCode = 404;
                    break;
                }
            });

            // Setup cleanup job to remove incomplete expired files.
            // This is just a simple example. In production one would use a cronjob/webjob and poll an endpoint that runs RemoveExpiredFilesAsync.
            var onAppDisposingToken = new OwinContext(app.Properties).Get <CancellationToken>("host.OnAppDisposing");

            Task.Run(async() =>
            {
                while (!onAppDisposingToken.IsCancellationRequested)
                {
                    Console.WriteLine("Running cleanup job...");
                    var numberOfRemovedFiles = await _tusDiskStore.RemoveExpiredFilesAsync(onAppDisposingToken);
                    Console.WriteLine(
                        $"Removed {numberOfRemovedFiles} expired files. Scheduled to run again in {_absoluteExpiration.Timeout.TotalMilliseconds} ms");
                    await Task.Delay(_absoluteExpiration.Timeout, onAppDisposingToken);
                }
            }, onAppDisposingToken);
        }