mirror of
https://gitlab.rlp.net/proj-wise2526-video2document/video2document.git
synced 2026-06-16 02:11:52 +02:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c00640c15d | |||
| d80941ca65 | |||
| 0e4147e893 | |||
| 9ab69b4b36 | |||
| cd61d8e09b | |||
| 893546d142 | |||
| 9098dafbd5 | |||
| d68192de8a | |||
| 5a23ec9c2f | |||
| a0237ade55 | |||
| 10e3c902c5 | |||
| b157a90671 | |||
| 6931df22e0 | |||
| 9eaabe80b6 | |||
| db2192dc30 | |||
| 76129982c3 | |||
| ea769d3aec | |||
| 21f4fe95d7 | |||
| 8e07bcf028 | |||
| fd0798872a | |||
| 0ea3fba436 | |||
| 5615f7fd25 |
@@ -88,27 +88,6 @@ Show your appreciation to those who have contributed to the project.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
For open source projects, say how it is licensed.
|
For open source projects, say how it is licensed.
|
||||||
---
|
|
||||||
|
|
||||||
## Sprint 4 – Secure API Key Management
|
## Project status
|
||||||
|
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||||
In Sprint 4, secure handling of API keys was implemented for the V2D (Video to Document) framework.
|
|
||||||
|
|
||||||
### Implementation Overview
|
|
||||||
- API keys are **not stored in the source code**
|
|
||||||
- The backend loads the key from an **environment variable**
|
|
||||||
- A single configuration works for all users without manual setup
|
|
||||||
- Secrets are protected from being exposed in the repository or frontend
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
The backend expects the following environment variable:
|
|
||||||
|
|
||||||
|
|
||||||
This variable is injected at runtime by the deployment or CI/CD environment and referenced in `application.yml`.
|
|
||||||
|
|
||||||
### Security Benefits
|
|
||||||
- Prevents accidental exposure of API keys
|
|
||||||
- Ensures secure collaboration in GitLab
|
|
||||||
- Follows best practices for secret management
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
Generate a structured meeting report in MARKDOWN using STRUCTURE and STYLE.
|
||||||
|
Output ONLY the final .md document — no meta comments, no explanations.
|
||||||
|
|
||||||
|
Follow exactly the STRUCTURE defined below.
|
||||||
|
Follow exactly the STYLE rules.
|
||||||
|
Use timestamps in HH:MM:SS format.
|
||||||
|
If information is missing, use: UNKLAR:<reason>.
|
||||||
|
|
||||||
|
==================== STRUCTURE & RULES ====================
|
||||||
|
|
||||||
|
{
|
||||||
|
"FORMAT": "markdown",
|
||||||
|
|
||||||
|
"STRUCTURE": {
|
||||||
|
"titlepage": [
|
||||||
|
"title",
|
||||||
|
"date",
|
||||||
|
"start",
|
||||||
|
"end",
|
||||||
|
"duration",
|
||||||
|
"location",
|
||||||
|
"host",
|
||||||
|
"participants"
|
||||||
|
],
|
||||||
|
|
||||||
|
"toc": "[section](#anchor) — HH:MM:SS",
|
||||||
|
|
||||||
|
"section": {
|
||||||
|
"h2": "<topic> — HH:MM:SS",
|
||||||
|
"summary": "exactly 1 concise sentence",
|
||||||
|
"key_points": "maximum 5 bullet points; quotes optional",
|
||||||
|
"decisions": "list items formatted as: decision text | owner | due date",
|
||||||
|
"actions": "markdown table: id | task | owner | due | status"
|
||||||
|
},
|
||||||
|
|
||||||
|
"exec_summary": "exactly 3 short sentences",
|
||||||
|
|
||||||
|
"consolidated": [
|
||||||
|
"decisions",
|
||||||
|
"actions"
|
||||||
|
],
|
||||||
|
|
||||||
|
"appendix": "optional"
|
||||||
|
},
|
||||||
|
|
||||||
|
"STYLE": {
|
||||||
|
"tone": "neutral, concise, professional",
|
||||||
|
"ts_format": "HH:MM:SS",
|
||||||
|
"no_meta": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"PROCESS": {
|
||||||
|
"timestamps": "use transcript timestamps if present; otherwise estimate minimal",
|
||||||
|
"speakers": "use names if available; else Speaker X",
|
||||||
|
"long_transcripts": "split → summarize → merge",
|
||||||
|
"unclear": "UNKLAR:<reason>"
|
||||||
|
},
|
||||||
|
|
||||||
|
"PROMPT_SNIPPET": "Generate meeting report in markdown using STRUCTURE and STYLE. Output only the report."
|
||||||
|
}
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Insert all generated content into the following MARKDOWN TEMPLATE:
|
||||||
|
|
||||||
|
# {{title}}
|
||||||
|
|
||||||
|
**Date:** {{date}}
|
||||||
|
**Start:** {{start}}
|
||||||
|
**End:** {{end}}
|
||||||
|
**Duration:** {{duration}}
|
||||||
|
**Location:** {{location}}
|
||||||
|
**Host:** {{host}}
|
||||||
|
**Participants:** {{participants}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
{{toc}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
{{exec_summary}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sections
|
||||||
|
{{sections}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Consolidated Decisions
|
||||||
|
{{consolidated_decisions}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Consolidated Actions
|
||||||
|
{{consolidated_actions}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix
|
||||||
|
{{appendix}}
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Final Requirement:
|
||||||
|
Output ONLY the completed Markdown meeting report.
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
spring:
|
|
||||||
application:
|
|
||||||
name: v2d-document
|
|
||||||
|
|
||||||
app:
|
|
||||||
external:
|
|
||||||
apiKey: ${LLM_API_KEY:}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.v2d.document.config;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConfigurationProperties(prefix = "app.external")
|
|
||||||
public class AppProperties {
|
|
||||||
private String apiKey;
|
|
||||||
|
|
||||||
public String getApiKey() { return apiKey; }
|
|
||||||
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package com.v2d.document.controller;
|
|
||||||
|
|
||||||
import com.v2d.document.service.ExternalApiService;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/generate")
|
|
||||||
public class GenerateController {
|
|
||||||
|
|
||||||
private final ExternalApiService externalApiService;
|
|
||||||
|
|
||||||
public GenerateController(ExternalApiService externalApiService) {
|
|
||||||
this.externalApiService = externalApiService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping
|
|
||||||
public ResponseEntity<String> generate(@RequestBody Map<String,Object> body) {
|
|
||||||
// Build provider payload from the user's body (transform safely)
|
|
||||||
String payload = "{\"text\": \"use this text\"}"; // adapt for real usage
|
|
||||||
String providerResponse = externalApiService.callProvider(payload);
|
|
||||||
return ResponseEntity.ok(providerResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package com.v2d.document.service;
|
|
||||||
|
|
||||||
import com.v2d.document.config.AppProperties;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class ExternalApiService {
|
|
||||||
private final Logger log = LoggerFactory.getLogger(ExternalApiService.class);
|
|
||||||
private final AppProperties props;
|
|
||||||
private final HttpClient http = HttpClient.newHttpClient();
|
|
||||||
|
|
||||||
public ExternalApiService(AppProperties props) {
|
|
||||||
this.props = props;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String callProvider(String jsonPayload) {
|
|
||||||
String key = props.getApiKey();
|
|
||||||
if (key == null || key.isBlank()) {
|
|
||||||
log.warn("External API key is not configured.");
|
|
||||||
throw new IllegalStateException("External API key missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpRequest req = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create("https://api.example.com/endpoint")) // replace with real endpoint
|
|
||||||
.header("Authorization", "Bearer " + key)
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
|
|
||||||
return resp.body();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("External API call failed: {}", e.getMessage());
|
|
||||||
throw new RuntimeException("External API call failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
package com.v2d.document.service;
|
|
||||||
|
|
||||||
import com.v2d.document.config.AppProperties;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class ExternalApiService {
|
|
||||||
private final Logger log = LoggerFactory.getLogger(ExternalApiService.class);
|
|
||||||
private final AppProperties props;
|
|
||||||
private final HttpClient http = HttpClient.newHttpClient();
|
|
||||||
|
|
||||||
public ExternalApiService(AppProperties props) {
|
|
||||||
this.props = props;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String callProvider(String jsonPayload) {
|
|
||||||
String key = props.getApiKey();
|
|
||||||
if (key == null || key.isBlank()) {
|
|
||||||
log.warn("External API key is not configured.");
|
|
||||||
throw new IllegalStateException("External API key missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpRequest req = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create("https://api.example.com/endpoint")) // replace with real endpoint
|
|
||||||
.header("Authorization", "Bearer " + key)
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
|
|
||||||
return resp.body();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("External API call failed: {}", e.getMessage());
|
|
||||||
throw new RuntimeException("External API call failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package com.v2d.document.config;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
public class AppPropertiesTest {
|
|
||||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
|
||||||
.withUserConfiguration(AppProperties.class)
|
|
||||||
.withPropertyValues("app.external.apiKey=TEST_KEY");
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void bindsApiKey() {
|
|
||||||
contextRunner.run(context -> {
|
|
||||||
AppProperties props = context.getBean(AppProperties.class);
|
|
||||||
assertThat(props.getApiKey()).isEqualTo("TEST_KEY");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user