Bug fixes (#184)

* Fix gist content when going back to editing

* Fix not outputting non-truncated large files for editon/zip download

* Allow dashes in usernames

* Delete keys associated to deleted user

* Fix error message when there is no files in gist

* Show if there is not files in gist preview

* Fix log parsing for the 11th empty commit
This commit is contained in:
Thomas Miceli 2023-12-27 12:11:02 +01:00
parent 3828022a1c
commit 3c97901995
13 changed files with 693 additions and 238 deletions

View file

@ -310,21 +310,21 @@ func (gist *Gist) DeleteRepository() error {
return git.DeleteRepository(gist.User.Username, gist.Uuid) return git.DeleteRepository(gist.User.Username, gist.Uuid)
} }
func (gist *Gist) Files(revision string) ([]*git.File, error) { func (gist *Gist) Files(revision string, truncate bool) ([]*git.File, error) {
var files []*git.File var files []*git.File
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, revision) filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, revision)
if err != nil { if err != nil {
// if the revision or the file do not exist // if the revision or the file do not exist
if exiterr, ok := err.(*exec.ExitError); ok && exiterr.ExitCode() == 128 { if exiterr, ok := err.(*exec.ExitError); ok && exiterr.ExitCode() == 128 {
return nil, nil return nil, &git.RevisionNotFoundError{}
} }
return nil, err return nil, err
} }
for _, fileStr := range filesStr { for _, fileStr := range filesStr {
file, err := gist.File(revision, fileStr, true) file, err := gist.File(revision, fileStr, truncate)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -53,6 +53,11 @@ func (user *User) BeforeDelete(tx *gorm.DB) error {
return err return err
} }
err = tx.Where("user_id = ?", user.ID).Delete(&SSHKey{}).Error
if err != nil {
return err
}
// Delete all gists created by this user // Delete all gists created by this user
return tx.Where("user_id = ?", user.ID).Delete(&Gist{}).Error return tx.Where("user_id = ?", user.ID).Delete(&Gist{}).Error
} }
@ -189,7 +194,7 @@ func (user *User) DeleteProviderID(provider string) error {
// -- DTO -- // // -- DTO -- //
type UserDTO struct { type UserDTO struct {
Username string `form:"username" validate:"required,max=24,alphanum,notreserved"` Username string `form:"username" validate:"required,max=24,alphanumdash,notreserved"`
Password string `form:"password" validate:"required"` Password string `form:"password" validate:"required"`
} }

View file

@ -22,6 +22,12 @@ var (
const truncateLimit = 2 << 18 const truncateLimit = 2 << 18
type RevisionNotFoundError struct{}
func (m *RevisionNotFoundError) Error() string {
return "revision not found"
}
func RepositoryPath(user string, gist string) string { func RepositoryPath(user string, gist string) string {
return filepath.Join(config.GetHomeDir(), ReposDirectory, strings.ToLower(user), gist) return filepath.Join(config.GetHomeDir(), ReposDirectory, strings.ToLower(user), gist)
} }

View file

@ -94,6 +94,11 @@ func parseLog(out io.Reader, maxBytes int) []*Commit {
scanner.Scan() scanner.Scan()
if len(scanner.Bytes()) == 0 {
commits = append(commits, currentCommit)
break
}
// if there is no shortstat, it means that the commit is empty, we add it and move onto the next one // if there is no shortstat, it means that the commit is empty, we add it and move onto the next one
if scanner.Bytes()[0] != ' ' { if scanner.Bytes()[0] != ' ' {
commits = append(commits, currentCommit) commits = append(commits, currentCommit)

View file

@ -25,7 +25,7 @@ gist.raw: Raw
gist.file-truncated: This file has been truncated. gist.file-truncated: This file has been truncated.
gist.watch-full-file: View the full file. gist.watch-full-file: View the full file.
gist.file-not-valid: This file is not a valid CSV file. gist.file-not-valid: This file is not a valid CSV file.
gist.no-content: No content gist.no-content: No files found
gist.new.new_gist: New gist gist.new.new_gist: New gist
gist.new.title: Title gist.new.title: Title

View file

@ -25,7 +25,7 @@ gist.raw: Brut
gist.file-truncated: Ce fichier a été tronqué. gist.file-truncated: Ce fichier a été tronqué.
gist.watch-full-file: Voir le fichier complet. gist.watch-full-file: Voir le fichier complet.
gist.file-not-valid: Ce fichier n'est pas un fichier CSV valide. gist.file-not-valid: Ce fichier n'est pas un fichier CSV valide.
gist.no-content: Pas de contenu gist.no-content: Aucun fichier
gist.new.new_gist: Nouveau gist gist.new.new_gist: Nouveau gist
gist.new.title: Titre gist.new.title: Titre

View file

@ -7,6 +7,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/git"
"github.com/thomiceli/opengist/internal/render" "github.com/thomiceli/opengist/internal/render"
"html/template" "html/template"
"net/url" "net/url"
@ -286,13 +287,11 @@ func gistIndex(ctx echo.Context) error {
revision = "HEAD" revision = "HEAD"
} }
files, err := gist.Files(revision) files, err := gist.Files(revision, true)
if err != nil { if _, ok := err.(*git.RevisionNotFoundError); ok {
return errorRes(500, "Error fetching files", err)
}
if len(files) == 0 {
return notFound("Revision not found") return notFound("Revision not found")
} else if err != nil {
return errorRes(500, "Error fetching files", err)
} }
renderedFiles, err := render.HighlightFiles(files) renderedFiles, err := render.HighlightFiles(files)
@ -310,7 +309,7 @@ func gistIndex(ctx echo.Context) error {
func gistJson(ctx echo.Context) error { func gistJson(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist) gist := getData(ctx, "gist").(*db.Gist)
files, err := gist.Files("HEAD") files, err := gist.Files("HEAD", true)
if err != nil { if err != nil {
return errorRes(500, "Error fetching files", err) return errorRes(500, "Error fetching files", err)
} }
@ -358,7 +357,7 @@ func gistJs(ctx echo.Context) error {
} }
gist := getData(ctx, "gist").(*db.Gist) gist := getData(ctx, "gist").(*db.Gist)
files, err := gist.Files("HEAD") files, err := gist.Files("HEAD", true)
if err != nil { if err != nil {
return errorRes(500, "Error fetching files", err) return errorRes(500, "Error fetching files", err)
} }
@ -481,7 +480,7 @@ func processCreate(ctx echo.Context) error {
if isCreate { if isCreate {
return html(ctx, "create.html") return html(ctx, "create.html")
} else { } else {
files, err := gist.Files("HEAD") files, err := gist.Files("HEAD", false)
if err != nil { if err != nil {
return errorRes(500, "Error fetching files", err) return errorRes(500, "Error fetching files", err)
} }
@ -690,7 +689,7 @@ func downloadFile(ctx echo.Context) error {
func edit(ctx echo.Context) error { func edit(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist) gist := getData(ctx, "gist").(*db.Gist)
files, err := gist.Files("HEAD") files, err := gist.Files("HEAD", false)
if err != nil { if err != nil {
return errorRes(500, "Error fetching files from repository", err) return errorRes(500, "Error fetching files from repository", err)
} }
@ -705,7 +704,7 @@ func downloadZip(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist) gist := getData(ctx, "gist").(*db.Gist)
revision := ctx.Param("revision") revision := ctx.Param("revision")
files, err := gist.Files(revision) files, err := gist.Files(revision, true)
if err != nil { if err != nil {
return errorRes(500, "Error fetching files from repository", err) return errorRes(500, "Error fetching files from repository", err)
} }

776
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -34,41 +34,43 @@ document.querySelectorAll('.md-code-copy-btn').forEach(button => {
}); });
let checkboxes = document.querySelectorAll('li[data-checkbox-nb] input[type=checkbox]'); let checkboxes = document.querySelectorAll('li[data-checkbox-nb] input[type=checkbox]');
document.querySelectorAll<HTMLElement>('li[data-checkbox-nb]').forEach((el) => { if (document.getElementById('gist').dataset.own) {
let input = el.firstElementChild; document.querySelectorAll<HTMLElement>('li[data-checkbox-nb]').forEach((el) => {
(input as HTMLButtonElement).disabled = false; let input: HTMLButtonElement = el.querySelector('input[type=checkbox]');
let checkboxNb = (el as HTMLElement).dataset.checkboxNb; input.disabled = false;
let filename = input.parentElement.parentElement.parentElement.parentElement.parentElement.dataset.file; let checkboxNb = (el as HTMLElement).dataset.checkboxNb;
let filename = input.closest<HTMLElement>('div[data-file]').dataset.file;
input.addEventListener('change', function () { input.addEventListener('change', function () {
const data = new URLSearchParams(); const data = new URLSearchParams();
data.append('checkbox', checkboxNb); data.append('checkbox', checkboxNb);
data.append('file', filename); data.append('file', filename);
if (document.getElementsByName('_csrf').length !== 0) { if (document.getElementsByName('_csrf').length !== 0) {
data.append('_csrf', ((document.getElementsByName('_csrf')[0] as HTMLInputElement).value)); data.append('_csrf', ((document.getElementsByName('_csrf')[0] as HTMLInputElement).value));
}
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = true;
el.classList.add('text-gray-400')
});
fetch(window.location.href.split('#')[0] + '/checkbox', {
method: 'PUT',
credentials: 'same-origin',
body: data,
}).then((response) => {
if (response.status === 200) {
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = false;
el.classList.remove('text-gray-400')
});
} }
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = true;
el.classList.add('text-gray-400')
});
fetch(window.location.href.split('#')[0] + '/checkbox', {
method: 'PUT',
credentials: 'same-origin',
body: data,
}).then((response) => {
if (response.status === 200) {
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = false;
el.classList.remove('text-gray-400')
});
}
});
}); });
}) });
} else {
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = true;
}) });
}

View file

@ -1,5 +1,5 @@
{{ define "gist_header" }} {{ define "gist_header" }}
<div class="py-10"> <div class="py-10" id="gist" data-own="{{ if .userLogged }}{{ if eq .gist.User.Username .userLogged.Username }}true{{ end }}{{ end }}">
<header> <header>
<div class="flex flex-col lg:flex-row"> <div class="flex flex-col lg:flex-row">
<div> <div>

View file

@ -151,23 +151,27 @@
<a href="{{ $.c.ExternalUrl }}/{{ $gist.User.Username }}/{{ $gist.Identifier }}" class="text-slate-700 dark:text-slate-300"> <a href="{{ $.c.ExternalUrl }}/{{ $gist.User.Username }}/{{ $gist.Identifier }}" class="text-slate-700 dark:text-slate-300">
<div class="rounded-md border border-1 border-gray-200 dark:border-gray-700 overflow-auto hover:border-primary-600"> <div class="rounded-md border border-1 border-gray-200 dark:border-gray-700 overflow-auto hover:border-primary-600">
<div class="code overflow-auto"> <div class="code overflow-auto">
{{ if isMarkdown $gist.PreviewFilename }} {{ if $gist.PreviewFilename }}
<div class="chroma preview markdown markdown-body p-8">{{ $gist.HTML | safe }}</div> {{ if isMarkdown $gist.PreviewFilename }}
{{ else }} <div class="chroma preview markdown markdown-body p-8">{{ $gist.HTML | safe }}</div>
<table class="chroma table-code w-full whitespace-pre" data-filename="{{ $gist.PreviewFilename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;"> {{ else }}
<tbody> <table class="chroma table-code w-full whitespace-pre" data-filename="{{ $gist.PreviewFilename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
{{ $ii := "1" }} <tbody>
{{ $i := toInt $ii }} {{ $ii := "1" }}
{{ range $line := $gist.Lines }} {{ $i := toInt $ii }}
{{ range $line := $gist.Lines }}
<tr> <tr>
<td class="select-none line-num px-4">{{$i}}</td> <td class="select-none line-num px-4">{{$i}}</td>
<td class="line-code">{{ $line | safe }}</td> <td class="line-code">{{ $line | safe }}</td>
</tr> </tr>
{{ $i = inc $i }} {{ $i = inc $i }}
{{ end }}
</tbody>
</table>
{{ end }} {{ end }}
</tbody> {{ else }}
</table> <div class="pl-4 py-0.5 text-xs"><p>{{ $.locale.Tr "gist.no-content" }}</p></div>
{{ end }} {{ end }}
</div> </div>
</div> </div>

View file

@ -55,7 +55,7 @@
</select> </select>
</div> </div>
</div> </div>
<input type="hidden" value="" name="content" class="form-filecontent"> <input type="hidden" value="" name="content" class="form-filecontent" autocomplete="off">
</div> </div>
</div> </div>

View file

@ -90,7 +90,7 @@
</select> </select>
</div> </div>
</div> </div>
<input type="hidden" value="{{ $file.Content }}" name="content" class="form-filecontent"> <input type="hidden" value="{{ $file.Content }}" name="content" class="form-filecontent" autocomplete="off">
</div> </div>
{{ end }} {{ end }}
</div> </div>