Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ Gigwa aims at managing genomic and genotyping data from NGS analyses. It is a to

Try Gigwa online with public datasets at https://gigwa.southgreen.fr/

## Latest webapp and bundles are now available for download on Gigwa homepage: https://www.southgreen.fr/content/gigwa
## Check more information about Gigwa on the project homepage:
[https://www.southgreen.fr/gigwa](https://www.southgreen.fr/gigwa)

## Check the wiki:
## Check the wiki for changelog:
https://github.com/SouthGreenPlatform/Gigwa2/wiki

## Developer instructions
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
tomcat:
depends_on:
- mongo
image: guilhemsempere/gigwa:2.11-RELEASE
image: guilhemsempere/gigwa:2.12-RELEASE
ports:
- 8080:8080 # You may amend the external port only (left hand side). By default, webapp URL will be http://host.ip:8080/gigwa
restart: always
Expand Down
2 changes: 1 addition & 1 deletion misc/linux_bundle.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

# Variables
project_version="2.11-RELEASE"
project_version="2.12-RELEASE"
tomcat_version="9.0.113"
mongodb_linux_file="linux-x86_64-ubuntu1804-4.2.25"
path_to_ubuntu_jre="zulu17.40.19-ca-jre17.0.6-linux_x64.tar.gz"
Expand Down
2 changes: 1 addition & 1 deletion misc/macos_bundle.command
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

# Variables
project_version="2.11-RELEASE"
project_version="2.12-RELEASE"
tomcat_version="9.0.113"
mongodb_osx_file="macos-x86_64-4.2.25"
path_to_osx_jre="zulu17.40.19-ca-jre17.0.6-macosx_x64.tar.gz"
Expand Down
2 changes: 1 addition & 1 deletion misc/win_bundle.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Variables
$project_version = "2.11-RELEASE"
$project_version = "2.12-RELEASE"
$tomcat_version = "9.0.113"
$mongodb_windows_file = "win32-x86_64-2012plus-4.2.25"
$path_to_windows_jre = "zulu17.40.19-ca-jre17.0.6-win_x64"
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<modelVersion>4.0.0</modelVersion>
<properties>
<project.version>2.11-RELEASE</project.version>
<project.version>2.12-RELEASE</project.version>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<bundle.dir>${project.build.directory}/bundles</bundle.dir>
Expand Down Expand Up @@ -52,7 +52,7 @@
<dependency>
<groupId>fr.cirad</groupId>
<artifactId>Mgdb2BrapiV2Impl</artifactId>
<version>2.1.1-RELEASE</version>
<version>2.1.2-RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
Expand All @@ -77,7 +77,7 @@
<dependency>
<groupId>fr.cirad</groupId>
<artifactId>Gigwa2ServiceImpl</artifactId>
<version>2.11-RELEASE</version>
<version>2.12-RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
Expand Down
161 changes: 161 additions & 0 deletions src/main/java/fr/cirad/web/controller/IGVProxyController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package fr.cirad.web.controller;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import fr.cirad.tools.AppConfig;

@Controller
@RequestMapping(IGVProxyController.IGV_PROXY_URL)
public class IGVProxyController {

public static final String IGV_PROXY_URL = "/igvProxy";

@Autowired private AppConfig appConfig;

private List<String> allowedDomains = null;
private List<String> wildcardDomains = null;

@GetMapping("/**")
public void proxyRequest(
@RequestParam("url") String targetUrl,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

if (allowedDomains == null) {
String allowedDomainsConfig = appConfig.get("igvProxiedDomains");
allowedDomains = Arrays.stream(allowedDomainsConfig.split(","))
.map(String::trim)
.filter(d -> !d.startsWith("*."))
.collect(Collectors.toList());

wildcardDomains = Arrays.stream(allowedDomainsConfig.split(","))
.map(String::trim)
.filter(d -> d.startsWith("*."))
.map(d -> d.substring(2)) // Remove '*.' prefix
.collect(Collectors.toList());
}

// Validate the URL against whitelist
if (!isUrlAllowed(targetUrl)) {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write("Domain not allowed for proxying");
return;
}

HttpURLConnection connection = null;
try {
URL url = new URL(targetUrl);
connection = (HttpURLConnection) url.openConnection();

// Set method
connection.setRequestMethod(request.getMethod());

// CRITICAL: Forward the Range header if present
String rangeHeader = request.getHeader("Range");
if (rangeHeader != null)
connection.setRequestProperty("Range", rangeHeader);

// Forward other headers (selectively)
String userAgent = request.getHeader("User-Agent");
if (userAgent != null) {
connection.setRequestProperty("User-Agent", userAgent);
}

// Connect and get response
connection.connect();
int responseCode = connection.getResponseCode();

// CRITICAL: Set proper status for partial content
if (responseCode == HttpURLConnection.HTTP_PARTIAL) {
response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
} else {
response.setStatus(responseCode);
}

// CRITICAL: Forward Content-Range header if present
String contentRange = connection.getHeaderField("Content-Range");
if (contentRange != null) {
response.setHeader("Content-Range", contentRange);
}

// Forward Accept-Ranges header if present
String acceptRanges = connection.getHeaderField("Accept-Ranges");
if (acceptRanges != null) {
response.setHeader("Accept-Ranges", acceptRanges);
}

// Forward Content-Length if present
String contentLength = connection.getHeaderField("Content-Length");
if (contentLength != null) {
response.setHeader("Content-Length", contentLength);
}

// Forward Content-Type
String contentType = connection.getHeaderField("Content-Type");
if (contentType != null) {
response.setHeader("Content-Type", contentType);
}

// Copy response body
InputStream inputStream;
if (responseCode >= 400)
inputStream = connection.getErrorStream();
else
inputStream = connection.getInputStream();

byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, bytesRead);
}

response.getOutputStream().flush();

} catch (Exception e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().write("Proxy error: " + e.getMessage());
} finally {
if (connection != null) {
connection.disconnect();
}
}
}

private boolean isUrlAllowed(String url) {
try {
URL targetUrl = new URL(url);
String host = targetUrl.getHost();

if (allowedDomains.contains(host)) {
return true;
}

for (String wildcardDomain : wildcardDomains) {
if (host.endsWith("." + wildcardDomain) || host.equals(wildcardDomain)) {
return true;
}
}

return false;
} catch (MalformedURLException e) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1429,27 +1429,7 @@ public ModelAndView setupImportPage()

return "";
}

// @ApiIgnore
// @ApiOperation(authorizations = { @Authorization(value = "AuthorizationToken") }, value = germplasmWithBrapiMappingURL, notes = "Lists IDs of germplasm with external reference source & ID")
// @GetMapping(value = BASE_URL + germplasmWithBrapiMappingURL)
// public @ResponseBody Collection<String> germplasmWithBrapiMappingURL(HttpServletRequest request, @RequestParam("module") final String sModule) {
// return MgdbDao.getInstance().loadIndividualsWithAllMetadata(sModule, AbstractTokenManager.getUserNameFromAuthentication(tokenManager.getAuthenticationFromToken(tokenManager.readToken(request))), null, null)
// .values().stream()
// .filter(ind -> ind.getAdditionalInfo().get(BrapiService.BRAPI_FIELD_externalReferenceSource) != null && ind.getAdditionalInfo().get(BrapiService.BRAPI_FIELD_externalReferenceId) != null)
// .map(ind -> sModule + Helper.ID_SEPARATOR + ind.getId()).toList();
// }
//
// @ApiIgnore
// @ApiOperation(authorizations = { @Authorization(value = "AuthorizationToken") }, value = samplesWithBrapiMappingURL, notes = "Lists IDs of samples with external reference source & ID")
// @GetMapping(value = BASE_URL + samplesWithBrapiMappingURL)
// public @ResponseBody Collection<String> samplesWithBrapiMappingURL(HttpServletRequest request, @RequestParam("module") final String sModule) {
// return MgdbDao.getInstance().loadSamplesWithAllMetadata(sModule, AbstractTokenManager.getUserNameFromAuthentication(tokenManager.getAuthenticationFromToken(tokenManager.readToken(request))), null, null)
// .values().stream()
// .filter(sp -> sp.getAdditionalInfo().get(BrapiService.BRAPI_FIELD_externalReferenceSource) != null && sp.getAdditionalInfo().get(BrapiService.BRAPI_FIELD_externalReferenceId) != null)
// .map(sp -> sModule + Helper.ID_SEPARATOR + sp.getId()).toList();
// }
//

private HashMap<String, String> getImportFilesByExtension(Collection<MultipartFile> importFiles, Collection<String> filesSpecifiedByURI) throws Exception{
HashMap<String, String> filesByExtension = new HashMap<>();
HashMap<String, String> synonymExtensions = new HashMap() {{ put("csv", "tsv"); put("phenotype", "tsv"); }}; // .phenotype is only synonym with tsv
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/config.default
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,7 @@ allowedOrigins_/**=*
maxPcaMatrixSize = 1E9

# Max dimension of the genotyping matrix (#samples x #SNPs) to calculate distance matrix on. Default is 1 billion (1E9)
maxDistanceMatrixSize = 1E9
maxDistanceMatrixSize = 1E9

# CSV list of domains (supporting wildcards) for which IGV.js will use a proxy to access genome / track files (workaround for CORS issues)
igvProxiedDomains = jbrowse.southgreen.fr
3 changes: 3 additions & 0 deletions src/main/resources/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,6 @@ maxPcaMatrixSize = 1E8

# Max dimension of the genotyping matrix (#samples x #SNPs) to calculate distance matrix on. Default is 1 billion (1E9)
maxDistanceMatrixSize = 1E8

# CSV list of domains (supporting wildcards) for which IGV.js will use a proxy to access genome / track files (workaround for CORS issues)
igvProxiedDomains = jbrowse.southgreen.fr
2 changes: 1 addition & 1 deletion src/main/webapp/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
Build-Jdk-Spec: 21
Created-By: Maven Integration for Eclipse
Implementation-version: 2.11-RELEASE
Implementation-version: 2.12-RELEASE
Class-Path:

16 changes: 16 additions & 0 deletions src/main/webapp/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,22 @@ div#termsOfUse .modal-lg {
width: 950px;
}

div#termsOfUse ol {
list-style-type: none;
padding-left: 0;
counter-reset: li-counter;
}

div#termsOfUse ol > li {
counter-increment: li-counter;
margin-bottom: 0.5em;
}

div#termsOfUse ol > li h4::before {
content: counter(li-counter) ") ";
margin-right: 0.2em;
}

div#chartDialog .modal-lg, div#individualFiltering .modal-lg, div#manual .modal-lg, div#variantDetailPanel .modal-lg, div#fjBytesPanel .modal-lg, div#genomeBrowserPanel .modal-lg, div#igvPanel .modal-lg {
width: 99%;
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/webapp/docs/gigwa_docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<BR>
<a href="javascript:window.print();" title="Print documentation"><IMG SRC="print.png" BORDER=0 style='float:right; margin-right:10px;'></a>
<P ALIGN=CENTER STYLE="margin-bottom: 0in; line-height: 100%;">
<FONT FACE="Arial, sans-serif"><FONT SIZE=6 class='maintitle'>Gigwa v2.11.x &ndash; Documentation</FONT></FONT>
<FONT FACE="Arial, sans-serif"><FONT SIZE=6 class='maintitle'>Gigwa v2.12.x &ndash; Documentation</FONT></FONT>
</P>
<p style='float:right; text-align:center; margin-top:20px; margin-right:20px; background-color:#f2f288; padding:15px; border:2px dashed #d5d800; border-radius:15px;'>
<b>Learn how to use Gigwa like a pro in a few minutes!</b>
Expand Down Expand Up @@ -582,6 +582,9 @@
<LI>
<P><B>maxDistanceMatrixSize</B> - Max dimension of the genotyping matrix (#samples x #SNPs) to calculate distance matrix on. Default is 1 billion (1E9)</P>
</LI>
<LI>
<P><B>igvProxiedDomains</B> - CSV list of domains (supporting wildcards) for which IGV.js will use a proxy to access genome / track files (workaround for CORS issues)</P>
</LI>
</UL>
</LI>
</UL>
Expand Down
Loading