mirror of
https://github.com/ordinary-dev/phoenix
synced 2024-09-19 19:30:28 +05:00
feat: export and import
The list of links can now be exported to a json file, and later imported back.
This commit is contained in:
parent
9a9a1af63e
commit
6396c160f2
|
@ -19,7 +19,8 @@ body {
|
|||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
button,
|
||||
textarea {
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
padding: 8px 10px;
|
||||
|
@ -35,11 +36,17 @@ button {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
input:hover,
|
||||
button:active,
|
||||
button:hover,
|
||||
button:focus {
|
||||
button:focus,
|
||||
textarea:focus,
|
||||
textarea:hover {
|
||||
border-color: #812abd;
|
||||
}
|
||||
|
||||
|
|
31
assets/css/import.css
vendored
Normal file
31
assets/css/import.css
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
body {
|
||||
padding: 2em 1em;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
body {
|
||||
padding: 2em 10em;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 400px;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
form textarea {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
form button {
|
||||
width: 100%;
|
||||
}
|
|
@ -2,6 +2,21 @@ body {
|
|||
padding: 2em 1em;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
|
||||
.actions a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.actions a img {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package database
|
||||
|
||||
type Group struct {
|
||||
ID int
|
||||
Name string
|
||||
Links []Link
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Links []Link `json:"links"`
|
||||
}
|
||||
|
||||
func GetGroupsWithLinks() ([]Group, error) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package database
|
||||
|
||||
type Link struct {
|
||||
ID int
|
||||
Name string
|
||||
Href string
|
||||
GroupID int
|
||||
Icon *string
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Href string `json:"href"`
|
||||
GroupID int `json:"-"`
|
||||
Icon *string `json:"icon,omitempty"`
|
||||
}
|
||||
|
||||
func GetLinksFromGroup(groupID int) ([]Link, error) {
|
||||
|
|
27
templates/import.html.tmpl
Normal file
27
templates/import.html.tmpl
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{{template "head" .}}
|
||||
<link rel="stylesheet" href="assets/css/import.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Import</h1>
|
||||
|
||||
<a href="/settings">Settings</a>
|
||||
|
||||
<p>
|
||||
Importing does not erase existing links, but may create duplicates.
|
||||
</p>
|
||||
|
||||
<form action="/import" method="POST">
|
||||
<label for="exportFile">JSON data</label>
|
||||
<textarea
|
||||
id="exportFile"
|
||||
name="exportFile"
|
||||
placeholder="{ groups: [] }"
|
||||
required
|
||||
></textarea>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -6,7 +6,18 @@
|
|||
</head>
|
||||
<body>
|
||||
<h1>Settings</h1>
|
||||
<a href="/">Main page</a>
|
||||
|
||||
<div class="actions">
|
||||
<a href="/">
|
||||
<img src="/assets/icons/solid/house.svg" width="20" height="20" /> Main page
|
||||
</a>
|
||||
<a href="/export">
|
||||
<img src="/assets/icons/solid/file-export.svg" width="20" height="20" /> Export links
|
||||
</a>
|
||||
<a href="/import">
|
||||
<img src="/assets/icons/solid/file-import.svg" width="20" height="20" /> Import links
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{range .groups}}
|
||||
<h2 id="group-{{.ID}}">Group "{{.Name}}"</h2>
|
||||
|
|
29
views/pages/export.go
Normal file
29
views/pages/export.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package pages
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/ordinary-dev/phoenix/database"
|
||||
)
|
||||
|
||||
type ExportFile struct {
|
||||
Groups []database.Group `json:"groups"`
|
||||
}
|
||||
|
||||
func Export(w http.ResponseWriter, _ *http.Request) {
|
||||
groups, err := database.GetGroupsWithLinks()
|
||||
if err != nil {
|
||||
ShowError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=phoenix.json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.Encode(&ExportFile{
|
||||
Groups: groups,
|
||||
})
|
||||
}
|
38
views/pages/import.go
Normal file
38
views/pages/import.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package pages
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/ordinary-dev/phoenix/database"
|
||||
)
|
||||
|
||||
func ImportPage(w http.ResponseWriter, _ *http.Request) {
|
||||
Render("import.html.tmpl", w, map[string]any{})
|
||||
}
|
||||
|
||||
func Import(w http.ResponseWriter, r *http.Request) {
|
||||
var exportFile ExportFile
|
||||
data := []byte(r.FormValue("exportFile"))
|
||||
if err := json.Unmarshal(data, &exportFile); err != nil {
|
||||
ShowError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, g := range exportFile.Groups {
|
||||
if err := database.CreateGroup(&g); err != nil {
|
||||
ShowError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, l := range g.Links {
|
||||
l.GroupID = g.ID
|
||||
if err := database.CreateLink(&l); err != nil {
|
||||
ShowError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
|
@ -53,6 +53,11 @@ func GetHttpServer() (*http.Server, error) {
|
|||
// Delete link.
|
||||
protectedMux.HandleFunc("POST /links/{id}/delete", pages.DeleteLink)
|
||||
|
||||
// Import-export
|
||||
protectedMux.HandleFunc("GET /export", pages.Export)
|
||||
protectedMux.HandleFunc("GET /import", pages.ImportPage)
|
||||
protectedMux.HandleFunc("POST /import", pages.Import)
|
||||
|
||||
return &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: middleware.LoggingMiddleware(
|
||||
|
|
Loading…
Reference in a new issue