mirror of
https://github.com/thomiceli/opengist.git
synced 2024-12-22 20:42:40 +00:00
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:
parent
3828022a1c
commit
3c97901995
13 changed files with 693 additions and 238 deletions
|
@ -310,21 +310,21 @@ func (gist *Gist) DeleteRepository() error {
|
|||
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
|
||||
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, revision)
|
||||
if err != nil {
|
||||
// if the revision or the file do not exist
|
||||
|
||||
if exiterr, ok := err.(*exec.ExitError); ok && exiterr.ExitCode() == 128 {
|
||||
return nil, nil
|
||||
return nil, &git.RevisionNotFoundError{}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, fileStr := range filesStr {
|
||||
file, err := gist.File(revision, fileStr, true)
|
||||
file, err := gist.File(revision, fileStr, truncate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -53,6 +53,11 @@ func (user *User) BeforeDelete(tx *gorm.DB) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = tx.Where("user_id = ?", user.ID).Delete(&SSHKey{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete all gists created by this user
|
||||
return tx.Where("user_id = ?", user.ID).Delete(&Gist{}).Error
|
||||
}
|
||||
|
@ -189,7 +194,7 @@ func (user *User) DeleteProviderID(provider string) error {
|
|||
// -- DTO -- //
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,12 @@ var (
|
|||
|
||||
const truncateLimit = 2 << 18
|
||||
|
||||
type RevisionNotFoundError struct{}
|
||||
|
||||
func (m *RevisionNotFoundError) Error() string {
|
||||
return "revision not found"
|
||||
}
|
||||
|
||||
func RepositoryPath(user string, gist string) string {
|
||||
return filepath.Join(config.GetHomeDir(), ReposDirectory, strings.ToLower(user), gist)
|
||||
}
|
||||
|
|
|
@ -94,6 +94,11 @@ func parseLog(out io.Reader, maxBytes int) []*Commit {
|
|||
|
||||
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 scanner.Bytes()[0] != ' ' {
|
||||
commits = append(commits, currentCommit)
|
||||
|
|
|
@ -25,7 +25,7 @@ gist.raw: Raw
|
|||
gist.file-truncated: This file has been truncated.
|
||||
gist.watch-full-file: View the full 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.title: Title
|
||||
|
|
|
@ -25,7 +25,7 @@ gist.raw: Brut
|
|||
gist.file-truncated: Ce fichier a été tronqué.
|
||||
gist.watch-full-file: Voir le fichier complet.
|
||||
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.title: Titre
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/thomiceli/opengist/internal/git"
|
||||
"github.com/thomiceli/opengist/internal/render"
|
||||
"html/template"
|
||||
"net/url"
|
||||
|
@ -286,13 +287,11 @@ func gistIndex(ctx echo.Context) error {
|
|||
revision = "HEAD"
|
||||
}
|
||||
|
||||
files, err := gist.Files(revision)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching files", err)
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
files, err := gist.Files(revision, true)
|
||||
if _, ok := err.(*git.RevisionNotFoundError); ok {
|
||||
return notFound("Revision not found")
|
||||
} else if err != nil {
|
||||
return errorRes(500, "Error fetching files", err)
|
||||
}
|
||||
|
||||
renderedFiles, err := render.HighlightFiles(files)
|
||||
|
@ -310,7 +309,7 @@ func gistIndex(ctx echo.Context) error {
|
|||
|
||||
func gistJson(ctx echo.Context) error {
|
||||
gist := getData(ctx, "gist").(*db.Gist)
|
||||
files, err := gist.Files("HEAD")
|
||||
files, err := gist.Files("HEAD", true)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching files", err)
|
||||
}
|
||||
|
@ -358,7 +357,7 @@ func gistJs(ctx echo.Context) error {
|
|||
}
|
||||
|
||||
gist := getData(ctx, "gist").(*db.Gist)
|
||||
files, err := gist.Files("HEAD")
|
||||
files, err := gist.Files("HEAD", true)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching files", err)
|
||||
}
|
||||
|
@ -481,7 +480,7 @@ func processCreate(ctx echo.Context) error {
|
|||
if isCreate {
|
||||
return html(ctx, "create.html")
|
||||
} else {
|
||||
files, err := gist.Files("HEAD")
|
||||
files, err := gist.Files("HEAD", false)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching files", err)
|
||||
}
|
||||
|
@ -690,7 +689,7 @@ func downloadFile(ctx echo.Context) error {
|
|||
func edit(ctx echo.Context) error {
|
||||
gist := getData(ctx, "gist").(*db.Gist)
|
||||
|
||||
files, err := gist.Files("HEAD")
|
||||
files, err := gist.Files("HEAD", false)
|
||||
if err != nil {
|
||||
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)
|
||||
revision := ctx.Param("revision")
|
||||
|
||||
files, err := gist.Files(revision)
|
||||
files, err := gist.Files(revision, true)
|
||||
if err != nil {
|
||||
return errorRes(500, "Error fetching files from repository", err)
|
||||
}
|
||||
|
|
776
package-lock.json
generated
776
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -34,41 +34,43 @@ document.querySelectorAll('.md-code-copy-btn').forEach(button => {
|
|||
});
|
||||
|
||||
let checkboxes = document.querySelectorAll('li[data-checkbox-nb] input[type=checkbox]');
|
||||
document.querySelectorAll<HTMLElement>('li[data-checkbox-nb]').forEach((el) => {
|
||||
let input = el.firstElementChild;
|
||||
(input as HTMLButtonElement).disabled = false;
|
||||
let checkboxNb = (el as HTMLElement).dataset.checkboxNb;
|
||||
let filename = input.parentElement.parentElement.parentElement.parentElement.parentElement.dataset.file;
|
||||
if (document.getElementById('gist').dataset.own) {
|
||||
document.querySelectorAll<HTMLElement>('li[data-checkbox-nb]').forEach((el) => {
|
||||
let input: HTMLButtonElement = el.querySelector('input[type=checkbox]');
|
||||
input.disabled = false;
|
||||
let checkboxNb = (el as HTMLElement).dataset.checkboxNb;
|
||||
let filename = input.closest<HTMLElement>('div[data-file]').dataset.file;
|
||||
|
||||
input.addEventListener('change', function () {
|
||||
const data = new URLSearchParams();
|
||||
data.append('checkbox', checkboxNb);
|
||||
data.append('file', filename);
|
||||
if (document.getElementsByName('_csrf').length !== 0) {
|
||||
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')
|
||||
});
|
||||
input.addEventListener('change', function () {
|
||||
const data = new URLSearchParams();
|
||||
data.append('checkbox', checkboxNb);
|
||||
data.append('file', filename);
|
||||
if (document.getElementsByName('_csrf').length !== 0) {
|
||||
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')
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
} else {
|
||||
checkboxes.forEach((el: HTMLButtonElement) => {
|
||||
el.disabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
2
templates/base/gist_header.html
vendored
2
templates/base/gist_header.html
vendored
|
@ -1,5 +1,5 @@
|
|||
{{ 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>
|
||||
<div class="flex flex-col lg:flex-row">
|
||||
<div>
|
||||
|
|
34
templates/pages/all.html
vendored
34
templates/pages/all.html
vendored
|
@ -151,23 +151,27 @@
|
|||
<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="code overflow-auto">
|
||||
{{ if isMarkdown $gist.PreviewFilename }}
|
||||
<div class="chroma preview markdown markdown-body p-8">{{ $gist.HTML | safe }}</div>
|
||||
{{ else }}
|
||||
<table class="chroma table-code w-full whitespace-pre" data-filename="{{ $gist.PreviewFilename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
|
||||
<tbody>
|
||||
{{ $ii := "1" }}
|
||||
{{ $i := toInt $ii }}
|
||||
{{ range $line := $gist.Lines }}
|
||||
{{ if $gist.PreviewFilename }}
|
||||
{{ if isMarkdown $gist.PreviewFilename }}
|
||||
<div class="chroma preview markdown markdown-body p-8">{{ $gist.HTML | safe }}</div>
|
||||
{{ else }}
|
||||
<table class="chroma table-code w-full whitespace-pre" data-filename="{{ $gist.PreviewFilename }}" style="font-size: 0.8em; border-spacing: 0; border-collapse: collapse;">
|
||||
<tbody>
|
||||
{{ $ii := "1" }}
|
||||
{{ $i := toInt $ii }}
|
||||
{{ range $line := $gist.Lines }}
|
||||
|
||||
<tr>
|
||||
<td class="select-none line-num px-4">{{$i}}</td>
|
||||
<td class="line-code">{{ $line | safe }}</td>
|
||||
</tr>
|
||||
{{ $i = inc $i }}
|
||||
<tr>
|
||||
<td class="select-none line-num px-4">{{$i}}</td>
|
||||
<td class="line-code">{{ $line | safe }}</td>
|
||||
</tr>
|
||||
{{ $i = inc $i }}
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ else }}
|
||||
<div class="pl-4 py-0.5 text-xs"><p>{{ $.locale.Tr "gist.no-content" }}</p></div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
2
templates/pages/create.html
vendored
2
templates/pages/create.html
vendored
|
@ -55,7 +55,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" value="" name="content" class="form-filecontent">
|
||||
<input type="hidden" value="" name="content" class="form-filecontent" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
2
templates/pages/edit.html
vendored
2
templates/pages/edit.html
vendored
|
@ -90,7 +90,7 @@
|
|||
</select>
|
||||
</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>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue