Merge pull request 'push-yrukqqxpolnk' (#3) from push-yrukqqxpolnk into main
Reviewed-on: #3
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"makefile.configureOnOpen": false
|
"makefile.configureOnOpen": false,
|
||||||
|
"git.enabled": false
|
||||||
}
|
}
|
||||||
@@ -7,17 +7,17 @@ import (
|
|||||||
|
|
||||||
func (d *DIDLLite) ToMarkdown() string {
|
func (d *DIDLLite) ToMarkdown() string {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
buf.WriteString("# DIDL-Lite Document\n\n")
|
buf.WriteString("### DIDL-Lite Document\n\n")
|
||||||
|
|
||||||
if len(d.Containers) > 0 {
|
if len(d.Containers) > 0 {
|
||||||
buf.WriteString("## Containers\n\n")
|
buf.WriteString("#### Containers\n\n")
|
||||||
for _, c := range d.Containers {
|
for _, c := range d.Containers {
|
||||||
c.markdown(&buf, 0)
|
c.markdown(&buf, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(d.Items) > 0 {
|
if len(d.Items) > 0 {
|
||||||
buf.WriteString("## Items\n\n")
|
buf.WriteString("#### Items\n\n")
|
||||||
for _, i := range d.Items {
|
for _, i := range d.Items {
|
||||||
i.markdown(&buf, 0)
|
i.markdown(&buf, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
/* 🎨 Dark theme (default) */
|
||||||
--bg-primary: #0d1117;
|
--bg-primary: #0d1117;
|
||||||
--bg-secondary: #161b22;
|
--bg-secondary: #161b22;
|
||||||
--border: #30363d;
|
--border: #30363d;
|
||||||
@@ -17,12 +18,26 @@
|
|||||||
--debug: #8957e5;
|
--debug: #8957e5;
|
||||||
--success: #3fb950;
|
--success: #3fb950;
|
||||||
}
|
}
|
||||||
|
body.light {
|
||||||
|
/* 🎨 Light theme */
|
||||||
|
--bg-primary: #f6f8fa;
|
||||||
|
--bg-secondary: #ffffff;
|
||||||
|
--border: #d0d7de;
|
||||||
|
--text-primary: #24292f;
|
||||||
|
--text-secondary: #57606a;
|
||||||
|
--error: #cf222e;
|
||||||
|
--warning: #9a6700;
|
||||||
|
--info: #0969da;
|
||||||
|
--debug: #8250df;
|
||||||
|
--success: #1a7f37;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
transition: background 0.3s, color 0.3s;
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -63,6 +78,7 @@
|
|||||||
border-left: 4px solid;
|
border-left: 4px solid;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: rgba(255, 255, 255, 0.03);
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
transition: background 0.3s;
|
||||||
}
|
}
|
||||||
.log.error { border-color: var(--error); }
|
.log.error { border-color: var(--error); }
|
||||||
.log.warning { border-color: var(--warning); }
|
.log.warning { border-color: var(--warning); }
|
||||||
@@ -86,7 +102,6 @@
|
|||||||
.level-debug { background: var(--debug); color: white; }
|
.level-debug { background: var(--debug); color: white; }
|
||||||
.log-content {
|
.log-content {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
white-space: pre-wrap;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono, Menlo, monospace;
|
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono, Menlo, monospace;
|
||||||
}
|
}
|
||||||
.log-fields {
|
.log-fields {
|
||||||
@@ -144,6 +159,97 @@
|
|||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 🔥 Markdown styling */
|
||||||
|
.log-content a {
|
||||||
|
color: var(--info);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.log-content details {
|
||||||
|
margin: 6px 0;
|
||||||
|
padding: 6px;
|
||||||
|
background: rgba(255,255,255,0.04);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.log-content pre code {
|
||||||
|
display: block;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.log-content h1,
|
||||||
|
.log-content h2,
|
||||||
|
.log-content h3 {
|
||||||
|
color: var(--info);
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.log-content ul {
|
||||||
|
margin-left: 20px;
|
||||||
|
list-style: disc;
|
||||||
|
margin-block-start: 0em;
|
||||||
|
margin-block-end: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-content p {
|
||||||
|
margin-block-start: 0em;
|
||||||
|
margin-block-end: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 🌗 Toggle theme button */
|
||||||
|
#theme-toggle {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#theme-toggle:hover {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style global des <details> */
|
||||||
|
.log details {
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding-left: 60px; /* Indentation à gauche */
|
||||||
|
border-left: 2px solid var(--border); /* Ligne pour visualiser l'indent */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style du <summary> */
|
||||||
|
.log summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
list-style: none;
|
||||||
|
color: var(--info);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Éviter que le triangle de <summary> déforme le texte */
|
||||||
|
.log summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optionnel : triangle personnalisé */
|
||||||
|
.log summary::before {
|
||||||
|
content: "▶️";
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 6px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Triangle pivotant quand ouvert */
|
||||||
|
.log details[open] summary::before {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Contenu du <details> */
|
||||||
|
.log details > *:not(summary) {
|
||||||
|
margin-left: 12px; /* Décalage supplémentaire pour le contenu interne */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
@@ -163,6 +269,7 @@
|
|||||||
<label><input type="checkbox" id="autoscroll" checked> Auto-scroll</label>
|
<label><input type="checkbox" id="autoscroll" checked> Auto-scroll</label>
|
||||||
<button id="clear">Clear</button>
|
<button id="clear">Clear</button>
|
||||||
<button id="export">Export</button>
|
<button id="export">Export</button>
|
||||||
|
<button id="theme-toggle">🌙 Dark</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="status">
|
<div id="status">
|
||||||
@@ -173,11 +280,25 @@
|
|||||||
</header>
|
</header>
|
||||||
<div id="logs"></div>
|
<div id="logs"></div>
|
||||||
<script>
|
<script>
|
||||||
|
// 🎨 Theme toggle
|
||||||
|
const themeToggle = document.getElementById("theme-toggle");
|
||||||
const logsContainer = document.getElementById('logs');
|
const logsContainer = document.getElementById('logs');
|
||||||
const statusIndicator = document.getElementById('status-indicator');
|
const statusIndicator = document.getElementById('status-indicator');
|
||||||
const statusText = document.getElementById('status-text');
|
const statusText = document.getElementById('status-text');
|
||||||
const logCountElement = document.getElementById('log-count');
|
const logCountElement = document.getElementById('log-count');
|
||||||
const maxLogs = 1000;
|
const maxLogs = 1000;
|
||||||
|
function updateTheme() {
|
||||||
|
if (document.body.classList.contains("light")) {
|
||||||
|
themeToggle.textContent = "🌙 Dark";
|
||||||
|
} else {
|
||||||
|
themeToggle.textContent = "☀️ Light";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
themeToggle.addEventListener("click", () => {
|
||||||
|
document.body.classList.toggle("light");
|
||||||
|
updateTheme();
|
||||||
|
});
|
||||||
|
updateTheme();
|
||||||
let eventSource = null;
|
let eventSource = null;
|
||||||
let filters = {
|
let filters = {
|
||||||
error: true,
|
error: true,
|
||||||
|
|||||||
69
soap/markdown.go
Normal file
69
soap/markdown.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package soap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Markdownable représente un objet pouvant se transformer en Markdown
|
||||||
|
type Markdownable interface {
|
||||||
|
ToMarkdown() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMarkdown convertit l'action et ses arguments en Markdown lisible
|
||||||
|
func (ar *ActionRequest) ToMarkdown() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Titre de l'action
|
||||||
|
buf.WriteString(fmt.Sprintf("➡️ SOAP Action: %s\n\n", ar.Name))
|
||||||
|
|
||||||
|
for key, val := range ar.Args {
|
||||||
|
buf.WriteString(fmt.Sprintf("- **%s**: ", key))
|
||||||
|
|
||||||
|
var content string
|
||||||
|
if md, ok := val.(Markdownable); ok {
|
||||||
|
content = md.ToMarkdown()
|
||||||
|
} else {
|
||||||
|
content = fmt.Sprintf("%v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si contenu long ou multi-lignes
|
||||||
|
if strings.Contains(content, "\n") || len(content) > 60 {
|
||||||
|
// Résumé : première ligne ou URL tronquée
|
||||||
|
summary := firstLineOrTruncate(content, 60)
|
||||||
|
buf.WriteString(fmt.Sprintf("`%s`\n", summary))
|
||||||
|
buf.WriteString("<details>\n\n")
|
||||||
|
|
||||||
|
// Conserver Markdown complet, sans indentation
|
||||||
|
buf.WriteString(content)
|
||||||
|
if !strings.HasSuffix(content, "\n") {
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
buf.WriteString("</details>\n\n")
|
||||||
|
} else if isURL(content) {
|
||||||
|
buf.WriteString(fmt.Sprintf("[%s](%s)\n", content, content))
|
||||||
|
} else {
|
||||||
|
buf.WriteString(fmt.Sprintf("`%s`\n", content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tronque la première ligne ou l'URL si trop longue
|
||||||
|
func firstLineOrTruncate(s string, max int) string {
|
||||||
|
lines := strings.SplitN(s, "\n", 2)
|
||||||
|
first := lines[0]
|
||||||
|
if len(first) > max {
|
||||||
|
return first[:max] + "…"
|
||||||
|
}
|
||||||
|
|
||||||
|
first = strings.TrimLeft(first, "# ")
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifie si une string ressemble à une URL
|
||||||
|
func isURL(s string) bool {
|
||||||
|
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
|
||||||
|
}
|
||||||
@@ -223,10 +223,7 @@ USN: uuid:%s::%s
|
|||||||
if _, err := s.conn.WriteToUDP([]byte(resp), src); err != nil {
|
if _, err := s.conn.WriteToUDP([]byte(resp), src); err != nil {
|
||||||
log.Warnf("❌ Failed to send M-SEARCH response to %v: %v", src, err)
|
log.Warnf("❌ Failed to send M-SEARCH response to %v: %v", src, err)
|
||||||
} else {
|
} else {
|
||||||
// log.Warnf("✅ M-Search response sent : %x", resp)
|
log.Infof("📡 Responded to M-SEARCH from %v with ST=%s\n<details>\n\n```\n%s\n```\n</details>\n\n", src, st, resp)
|
||||||
// log.Warnf("✅ M-Search response sent : %s", resp)
|
|
||||||
|
|
||||||
log.Infof("📡 Responded to M-SEARCH from %v with ST=%s", src, st)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func _AVTransportURIMetaDataParser(value string) (interface{}, error) {
|
func _AVTransportURIMetaDataParser(value string) (interface{}, error) {
|
||||||
log.Warnf("[avtransport] Parsing AVTransport)")
|
log.Debug("[avtransport] Parsing AVTransport)")
|
||||||
didl, err := didl.Parse(value)
|
didl, err := didl.Parse(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return value, err
|
return value, err
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func (svc *ServiceInstance) ControlHandler() func(w http.ResponseWriter, r *http
|
|||||||
// 1️⃣ Chercher si le param correspond à une StateVarInstance
|
// 1️⃣ Chercher si le param correspond à une StateVarInstance
|
||||||
|
|
||||||
sv, ok := svc.statevariables[param]
|
sv, ok := svc.statevariables[param]
|
||||||
log.Warnf("Look for a variable named : %s -> %v", param, ok)
|
log.Debugf("Look for a variable named : %s -> %v", param, ok)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
if sv.HasParser() {
|
if sv.HasParser() {
|
||||||
@@ -221,13 +221,7 @@ func (svc *ServiceInstance) ControlHandler() func(w http.ResponseWriter, r *http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("➡️ SOAP Action: %s", req.Name)
|
log.Info(req.ToMarkdown())
|
||||||
for k, v := range req.Args {
|
|
||||||
if mi, ok := v.(Markdownable); ok {
|
|
||||||
v = mi.ToMarkdown()
|
|
||||||
}
|
|
||||||
log.Infof(" %s:%s = %v", req.Name, k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ici tu peux appeler l'action correspondante sur svc.actions[req.Name] et récupérer le résultat
|
// Ici tu peux appeler l'action correspondante sur svc.actions[req.Name] et récupérer le résultat
|
||||||
// Exemple de réponse minimale :
|
// Exemple de réponse minimale :
|
||||||
|
|||||||
Reference in New Issue
Block a user