Compare commits

..

3 Commits
0.16.3 ... main

Author SHA1 Message Date
71161a4da1 Changing API design and making tests compatible
All checks were successful
sample gradle build and test / build (pull_request) Successful in 2m6s
sample gradle build and test / tag (push) Successful in 7s
sample gradle build and test / build_tag_push (push) Successful in 3m5s
Daily cloudflare API integration test / cloudflare-api-test (push) Successful in 1m33s
Promote image with tag test to prod / tag (push) Successful in 7s
Promote image with tag test to prod / build_tag_push (push) Successful in 13s
2026-01-28 00:21:13 +05:30
c1ea9c4197 Fix database URL configuration for docker-compose compatibility
All checks were successful
sample gradle build and test / build (pull_request) Successful in 2m30s
sample gradle build and test / tag (push) Successful in 5s
sample gradle build and test / build_tag_push (push) Successful in 3m5s
Daily cloudflare API integration test / cloudflare-api-test (push) Successful in 1m50s
Promote image with tag test to prod / tag (push) Successful in 8s
Promote image with tag test to prod / build_tag_push (push) Successful in 13s
- Hardcode PostgreSQL connection URL to match docker service name 'postgres'
- Remove DB_URL environment variable dependency that was causing startup failures
- Keep username/password as environment variables for flexibility
2026-01-23 23:23:39 +05:30
e9675db11a Remove fork sync job from test build workflow
- Remove sync_forks job that was causing issues
- Fork syncing doesn't work reliably in current setup
- Keep core build and deployment functionality
2026-01-23 21:37:22 +05:30
5 changed files with 14 additions and 31 deletions

View File

@ -64,23 +64,4 @@ jobs:
run: | run: |
docker push 192.168.0.100:8928/hithomelabs/cftunnels:test docker push 192.168.0.100:8928/hithomelabs/cftunnels:test
docker push 192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} docker push 192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }}
sync_forks:
name: Sync All Forks
runs-on: ubuntu-latest
needs: build_tag_push
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Sync all forks via Gitea API
run: |
echo "Fetching forks for Hithomelabs/CFTunnels..."
response=$(curl -s -X GET "https://gitea.hithomelabs.com/api/v1/repos/Hithomelabs/CFTunnels/forks" -H "Authorization: token ${{secrets.TOKEN}}")
filtered=$(echo "$response" | grep -o '"clone_url":"[^"]*"' | sed 's/"clone_url":"\([^"]*\)"/\1/' | grep -v "/Hithomelabs")
echo "Detected forks:"
echo "$filtered"
readarray -t forks <<< "$filtered"
for fork_url in "${forks[@]}"; do
echo "🔄 Syncing fork: $fork_url"
authed_url=$(echo "$fork_url" | sed "s#https://#https://${{secrets.TOKEN}}@#")
git push "$authed_url" test &
done

View File

@ -11,6 +11,8 @@ import com.hithomelabs.CFTunnels.Models.Ingress;
import com.hithomelabs.CFTunnels.Models.TunnelResponse; import com.hithomelabs.CFTunnels.Models.TunnelResponse;
import com.hithomelabs.CFTunnels.Services.CloudflareAPIService; import com.hithomelabs.CFTunnels.Services.CloudflareAPIService;
import com.hithomelabs.CFTunnels.Services.MappingRequestService; import com.hithomelabs.CFTunnels.Services.MappingRequestService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.*; import org.springframework.http.*;
@ -64,6 +66,7 @@ public class TunnelController implements ErrorController {
@PreAuthorize("hasAnyRole('USER')") @PreAuthorize("hasAnyRole('USER')")
@GetMapping("/tunnels") @GetMapping("/tunnels")
@Operation( security = { @SecurityRequirement(name = "oidcAuth") } )
public ResponseEntity<Map<String,Object>> getTunnels(){ public ResponseEntity<Map<String,Object>> getTunnels(){
ResponseEntity<Map> responseEntity = cloudflareAPIService.getCloudflareTunnels(); ResponseEntity<Map> responseEntity = cloudflareAPIService.getCloudflareTunnels();
@ -75,7 +78,7 @@ public class TunnelController implements ErrorController {
} }
@PreAuthorize("hasAnyRole('DEVELOPER')") @PreAuthorize("hasAnyRole('DEVELOPER')")
@GetMapping("/tunnel/{tunnelId}") @GetMapping("/tunnel/{tunnelId}/mappings")
public ResponseEntity<Map<String,Object>> getTunnelConfigurations(@PathVariable String tunnelId) { public ResponseEntity<Map<String,Object>> getTunnelConfigurations(@PathVariable String tunnelId) {
ResponseEntity<Map> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplate, Map.class); ResponseEntity<Map> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplate, Map.class);
@ -88,7 +91,7 @@ public class TunnelController implements ErrorController {
// 50df9101-f625-4618-b7c5-100338a57124 // 50df9101-f625-4618-b7c5-100338a57124
@PreAuthorize("hasAnyRole('ADMIN')") @PreAuthorize("hasAnyRole('ADMIN')")
@PutMapping("/tunnel/{tunnelId}/add") @PostMapping("/tunnel/{tunnelId}/mappings")
public ResponseEntity<Map<String, Object>> addTunnelconfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException { public ResponseEntity<Map<String, Object>> addTunnelconfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class); ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class);
@ -110,7 +113,7 @@ public class TunnelController implements ErrorController {
} }
@PreAuthorize("hasAnyRole('DEVELOPER')") @PreAuthorize("hasAnyRole('DEVELOPER')")
@PutMapping("/tunnel/{tunnelId}/delete") @DeleteMapping("/tunnel/{tunnelId}/mappings")
public ResponseEntity<Map<String, Object>> deleteTunnelConfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException { public ResponseEntity<Map<String, Object>> deleteTunnelConfiguration(@PathVariable String tunnelId, @RequestBody Ingress ingress) throws JsonProcessingException {
ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class); ResponseEntity<TunnelResponse> responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(tunnelId, restTemplateConfig.restTemplate(), TunnelResponse.class);
@ -139,7 +142,7 @@ public class TunnelController implements ErrorController {
} }
@PreAuthorize("hasAnyRole('DEVELOPER')") @PreAuthorize("hasAnyRole('DEVELOPER')")
@PutMapping("/tunnel/{tunnelId}/request") @PostMapping("/tunnel/{tunnelId}/requests")
public ResponseEntity<Request> createTunnelMappingRequest(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser oidcUser, @RequestBody Ingress ingess){ public ResponseEntity<Request> createTunnelMappingRequest(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser oidcUser, @RequestBody Ingress ingess){
Request request = mappingRequestService.createMappingRequest(tunnelId, ingess, oidcUser); Request request = mappingRequestService.createMappingRequest(tunnelId, ingess, oidcUser);
if(request.getId() != null) if(request.getId() != null)

View File

@ -1,7 +1,7 @@
api.baseUrl=https://cftunnels.hithomelabs.com api.baseUrl=https://cftunnels.hithomelabs.com
# Production Database Configuration # Production Database Configuration
spring.datasource.url=${DB_URL} spring.datasource.url=jdbc:postgresql://postgres:5432/cftunnel
spring.datasource.username=${POSTGRES_USERNAME} spring.datasource.username=${POSTGRES_USERNAME}
spring.datasource.password=${POSTGRES_PASSWORD} spring.datasource.password=${POSTGRES_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name=org.postgresql.Driver

View File

@ -1,7 +1,7 @@
api.baseUrl=https://testcf.hithomelabs.com api.baseUrl=https://testcf.hithomelabs.com
# Test Database Configuration - Same as Production # Test Database Configuration - Same as Production
spring.datasource.url=${DB_URL} spring.datasource.url=jdbc:postgresql://postgres:5432/cftunnel
spring.datasource.username=${POSTGRES_USERNAME} spring.datasource.username=${POSTGRES_USERNAME}
spring.datasource.password=${POSTGRES_PASSWORD} spring.datasource.password=${POSTGRES_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.driver-class-name=org.postgresql.Driver

View File

@ -36,8 +36,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
@ -162,7 +161,7 @@ class TunnelControllerTest {
when(cloudflareAPIService.getCloudflareTunnelConfigurations(eq("sampleTunnelId"), any(RestTemplate.class), eq(Map.class))).thenReturn(mockResponse); when(cloudflareAPIService.getCloudflareTunnelConfigurations(eq("sampleTunnelId"), any(RestTemplate.class), eq(Map.class))).thenReturn(mockResponse);
mockMvc.perform(get("/cloudflare/tunnel/{tunnelId}", "sampleTunnelId") mockMvc.perform(get("/cloudflare/tunnel/{tunnelId}/mappings", "sampleTunnelId")
.with(oauth2Login().oauth2User(buildOidcUser("username", Groups.HOMELAB_DEVELOPER)))) .with(oauth2Login().oauth2User(buildOidcUser("username", Groups.HOMELAB_DEVELOPER))))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
@ -184,7 +183,7 @@ class TunnelControllerTest {
ResponseEntity<TunnelResponse> expectedHttpTunnelResponse = new ResponseEntity<>(expectedTunnelConfig, HttpStatus.OK); ResponseEntity<TunnelResponse> expectedHttpTunnelResponse = new ResponseEntity<>(expectedTunnelConfig, HttpStatus.OK);
when(cloudflareAPIService.putCloudflareTunnelConfigurations(eq("sampleTunnelId"), any(RestTemplate.class), eq(TunnelResponse.class), any(Config.class))).thenReturn(expectedHttpTunnelResponse); when(cloudflareAPIService.putCloudflareTunnelConfigurations(eq("sampleTunnelId"), any(RestTemplate.class), eq(TunnelResponse.class), any(Config.class))).thenReturn(expectedHttpTunnelResponse);
mockMvc.perform(put("/cloudflare/tunnel/{tunnelId}/add", "sampleTunnelId") mockMvc.perform(post("/cloudflare/tunnel/{tunnelId}/mappings", "sampleTunnelId")
.with(oauth2Login().oauth2User(buildOidcUser("admin", Groups.SYSTEM_ADMIN))) .with(oauth2Login().oauth2User(buildOidcUser("admin", Groups.SYSTEM_ADMIN)))
.with(csrf()) .with(csrf())
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
@ -209,7 +208,7 @@ class TunnelControllerTest {
ResponseEntity<TunnelResponse> expectedHttpTunnelResponse = new ResponseEntity<>(expectedTunnelConfig, HttpStatus.OK); ResponseEntity<TunnelResponse> expectedHttpTunnelResponse = new ResponseEntity<>(expectedTunnelConfig, HttpStatus.OK);
when(cloudflareAPIService.putCloudflareTunnelConfigurations(eq("sampleTunnelId"), any(RestTemplate.class), eq(TunnelResponse.class), any(Config.class))).thenReturn(expectedHttpTunnelResponse); when(cloudflareAPIService.putCloudflareTunnelConfigurations(eq("sampleTunnelId"), any(RestTemplate.class), eq(TunnelResponse.class), any(Config.class))).thenReturn(expectedHttpTunnelResponse);
mockMvc.perform(put("/cloudflare/tunnel/{tunnelId}/delete", "sampleTunnelId") mockMvc.perform(delete("/cloudflare/tunnel/{tunnelId}/mappings", "sampleTunnelId")
.with(oauth2Login().oauth2User(buildOidcUser("admin", Groups.SYSTEM_ADMIN))) .with(oauth2Login().oauth2User(buildOidcUser("admin", Groups.SYSTEM_ADMIN)))
.with(csrf()) .with(csrf())
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)