Compare commits
No commits in common. "2cdee0eed1cc81682eb6268ada2a20ee0d2759f6" and "9cfa861bb9aadcf3f7bf231e96b091df0e767adc" have entirely different histories.
2cdee0eed1
...
9cfa861bb9
@ -220,9 +220,6 @@ func RoutesProtected(r *mux.Router, bd *gorm.DB) {
|
|||||||
//API Check path
|
//API Check path
|
||||||
r.HandleFunc("/validate-path", download.PathValidationHandler)
|
r.HandleFunc("/validate-path", download.PathValidationHandler)
|
||||||
|
|
||||||
r.HandleFun("/folders", renders.StreamHandler)
|
|
||||||
r.HandleFun("/folders/detail", renders.DetailHandler)
|
|
||||||
|
|
||||||
//API Scan folder
|
//API Scan folder
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,58 +20,7 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// templates contiendra TOUTES vos pages .pages.tmpl
|
|
||||||
templates *template.Template
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// 1. Définissez votre FuncMap
|
|
||||||
funcMap := template.FuncMap{
|
|
||||||
"hasSuffix": strings.HasSuffix,
|
|
||||||
"ext": func(name string) string {
|
|
||||||
return strings.TrimPrefix(filepath.Ext(name), ".")
|
|
||||||
},
|
|
||||||
"split": strings.Split,
|
|
||||||
"trimPrefix": strings.TrimPrefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Parsez tous les templates EN UNE FOIS, en injectant le FuncMap
|
|
||||||
templates = template.Must(
|
|
||||||
template.New("").
|
|
||||||
Funcs(funcMap).
|
|
||||||
// ajuste le chemin vers vos .pages.tmpl
|
|
||||||
ParseGlob("./templates/*.pages.tmpl"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Entry struct {
|
|
||||||
Name, Path string
|
|
||||||
IsDir bool
|
|
||||||
ModTime time.Time
|
|
||||||
Size int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper pour lister un dossier
|
|
||||||
func listEntries(base, rel string) ([]Entry, error) {
|
|
||||||
dir := filepath.Join(base, rel)
|
|
||||||
fis, err := os.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out := make([]Entry, 0, len(fis))
|
|
||||||
for _, fi := range fis {
|
|
||||||
info, _ := fi.Info()
|
|
||||||
out = append(out, Entry{
|
|
||||||
Name: fi.Name(),
|
|
||||||
Path: filepath.ToSlash(filepath.Join(rel, fi.Name())),
|
|
||||||
IsDir: fi.IsDir(),
|
|
||||||
ModTime: info.ModTime(),
|
|
||||||
Size: info.Size(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
func Login(w http.ResponseWriter, r *http.Request){
|
func Login(w http.ResponseWriter, r *http.Request){
|
||||||
renderTemplate(w,"login",nil)
|
renderTemplate(w,"login",nil)
|
||||||
}
|
}
|
||||||
@ -627,72 +576,37 @@ func HandleDeleteMultipleJobs(db *gorm.DB) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StreamHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
base := "/app/upload"
|
|
||||||
cur := r.URL.Query().Get("path") // ex: "", "Icons", "Code/Sub"
|
|
||||||
|
|
||||||
// 1) sidebar : on ne gère que le niveau racine
|
|
||||||
root, _ := listEntries(base, "")
|
|
||||||
var dirs []Entry
|
|
||||||
for _, e := range root {
|
|
||||||
if e.IsDir {
|
|
||||||
dirs = append(dirs, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) contenu courant
|
|
||||||
entries, _ := listEntries(base, cur)
|
|
||||||
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"Dirs": dirs,
|
|
||||||
"Entries": entries,
|
|
||||||
"CurrentPath": cur,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si c’est un appel HTMX (liste partielle) : on renvoie juste la grille
|
|
||||||
if r.Header.Get("HX-Request") == "true" {
|
|
||||||
renderPartial(w, "_file_list", data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Sinon on rend la page complète
|
|
||||||
renderTemplate(w, "folders", data)
|
|
||||||
}
|
|
||||||
func DetailHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
base := "static/uploads"
|
|
||||||
rel := r.URL.Query().Get("path")
|
|
||||||
info, err := os.Stat(filepath.Join(base, rel))
|
|
||||||
if err != nil {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
entry := Entry{
|
|
||||||
Name: info.Name(),
|
|
||||||
Path: rel,
|
|
||||||
IsDir: info.IsDir(),
|
|
||||||
ModTime: info.ModTime(),
|
|
||||||
Size: info.Size(),
|
|
||||||
}
|
|
||||||
// Toujours partial HTMX
|
|
||||||
renderPartial(w, "_file_detail", map[string]interface{}{
|
|
||||||
"Entry": entry,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func renderPartial(w http.ResponseWriter, templ string, data map[string]interface{}) {
|
func renderPartial(w http.ResponseWriter, templ string, data map[string]interface{}) {
|
||||||
// Exécute directement le define `<templ>.pages.tmpl`
|
t, err := template.ParseFiles("./templates/" + templ + ".pages.tmpl")
|
||||||
if err := templates.ExecuteTemplate(w, templ+".pages.tmpl", data); err != nil {
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Execute(w, data)
|
||||||
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func renderTemplate(w http.ResponseWriter, templ string, data map[string]interface{}) {
|
func renderTemplate(w http.ResponseWriter, templ string, data map[string]interface{}) {
|
||||||
// Pareil, on exécute le principal
|
t, err := template.ParseFiles(
|
||||||
if err := templates.ExecuteTemplate(w, templ+".pages.tmpl", data); err != nil {
|
"./templates/head.pages.tmpl", // Template inclus
|
||||||
|
"./templates/" + templ + ".pages.tmpl", // Template principal
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exécutez explicitement le template principal
|
||||||
|
err = t.ExecuteTemplate(w, templ+".pages.tmpl", data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
<div class="box">
|
|
||||||
{{ with .Entry }}
|
|
||||||
<h4 class="title is-5">{{ .Name }}</h4>
|
|
||||||
<p><strong>Type :</strong> {{ ext .Name }}</p>
|
|
||||||
<p><strong>Taille :</strong> {{ printf "%.1f KB" (float64 .Size/1024) }}</p>
|
|
||||||
<p><strong>Modifié le :</strong> {{ .ModTime.Format "02 Jan 2006 15:04" }}</p>
|
|
||||||
{{ if and (not .IsDir) (or (hasSuffix .Name ".jpg") (hasSuffix .Name ".png")) }}
|
|
||||||
<figure class="image">
|
|
||||||
<img src="/app/upload/{{ .Path }}">
|
|
||||||
</figure>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
<div class="columns is-multiline">
|
|
||||||
{{- range .Entries }}
|
|
||||||
<div class="column is-4-mobile is-3-tablet">
|
|
||||||
<div class="card"
|
|
||||||
hx-get="{{ if .IsDir }}
|
|
||||||
/folders?path={{ urlquery .Path }}
|
|
||||||
{{ else }}
|
|
||||||
/folders/detail?path={{ urlquery .Path }}
|
|
||||||
{{ end }}"
|
|
||||||
hx-target="{{ if .IsDir }}#file-list{{ else }}#detail-panel{{ end }}"
|
|
||||||
hx-push-url="true"
|
|
||||||
style="cursor:pointer">
|
|
||||||
<div class="card-image">
|
|
||||||
<figure class="image is-4by3">
|
|
||||||
{{ if .IsDir }}
|
|
||||||
<i class="fas fa-folder fa-3x has-text-warning" style="margin:1rem"></i>
|
|
||||||
{{ else if or (hasSuffix .Name ".jpg") (hasSuffix .Name ".png") }}
|
|
||||||
<img src="/app/upload/{{ .Path }}" alt="{{ .Name }}">
|
|
||||||
{{ else }}
|
|
||||||
<i class="fas fa-file fa-3x"></i>
|
|
||||||
{{ end }}
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
<div class="card-content">
|
|
||||||
<p class="title is-6">{{ .Name }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{- end }}
|
|
||||||
</div>
|
|
||||||
@ -43,7 +43,6 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a hx-get="/folders" class="nav-link" hx-target="#content" hx-swap-oob="beforeend">Folders</a></li>
|
|
||||||
<li><a hx-get="/settings" class="nav-link" hx-target="#content" hx-swap-oob="beforeend">Settings</a></li>
|
<li><a hx-get="/settings" class="nav-link" hx-target="#content" hx-swap-oob="beforeend">Settings</a></li>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -1,80 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
<section class="section">
|
|
||||||
<div class="container is-fluid">
|
|
||||||
<div class="columns">
|
|
||||||
|
|
||||||
<!-- Sidebar -->
|
|
||||||
<div class="column is-2">
|
|
||||||
<aside class="menu">
|
|
||||||
<p class="menu-label">Dossiers</p>
|
|
||||||
<ul class="menu-list">
|
|
||||||
{{- range .Dirs }}
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
hx-get="/folders?path={{ urlquery .Name }}"
|
|
||||||
hx-target="#file-list"
|
|
||||||
hx-push-url="true"
|
|
||||||
class="{{ if eq $.CurrentPath .Name }}is-active{{ end }}"
|
|
||||||
>
|
|
||||||
{{ .Name }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{- end }}
|
|
||||||
</ul>
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Grille -->
|
|
||||||
<div class="column is-7">
|
|
||||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
hx-get="/folders"
|
|
||||||
hx-target="#file-list"
|
|
||||||
hx-push-url="true"
|
|
||||||
>
|
|
||||||
Home
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{- $parts := split .CurrentPath "/" }}
|
|
||||||
{{- $acc := "" }}
|
|
||||||
{{- range $i, $p := $parts }}
|
|
||||||
{{- if ne $p "" }}
|
|
||||||
{{- $acc = printf "%s/%s" $acc $p }}
|
|
||||||
<li class="is-active">
|
|
||||||
<a
|
|
||||||
hx-get="/folders?path={{ urlquery (trimPrefix "/" $acc) }}"
|
|
||||||
hx-target="#file-list"
|
|
||||||
hx-push-url="true"
|
|
||||||
>
|
|
||||||
{{ $p }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="file-list"
|
|
||||||
hx-trigger="load"
|
|
||||||
hx-get="/folders?path={{ urlquery .CurrentPath }}"
|
|
||||||
hx-target="#file-list"
|
|
||||||
>
|
|
||||||
{{ template "_file_list.pages.tmpl" . }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Détails -->
|
|
||||||
<div class="column is-3">
|
|
||||||
<div id="detail-panel">
|
|
||||||
<em>Sélectionnez un fichier ou dossier…</em>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user