Skip to content

Commit

Permalink
feat(gateway): preview dag-cbor/-json with links
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed May 26, 2023
1 parent ab987b2 commit d49baa8
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 52 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The following emojis are used to highlight certain changes:
- New human-friendly error messages.
- Updated, higher-definition icons in directory listings.
- Customizable menu items next to "About IPFS" and "Install IPFS".
- Valid DAG-CBOR and DAG-JSON blocks now provide a preview, where links can be followed.

## [0.8.0] - 2023-04-05
### Added
Expand Down
7 changes: 4 additions & 3 deletions gateway/assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ type MenuItem struct {
}

type GlobalData struct {
Menu []MenuItem
Menu []MenuItem
GatewayURL string
DNSLink bool
}

type DagTemplateData struct {
Expand All @@ -95,6 +97,7 @@ type DagTemplateData struct {
CID string
CodecName string
CodecHex string
Node *ParsedNode
}

type ErrorTemplateData struct {
Expand All @@ -106,8 +109,6 @@ type ErrorTemplateData struct {

type DirectoryTemplateData struct {
GlobalData
GatewayURL string
DNSLink bool
Listing []DirectoryItem
Size string
Path string
Expand Down
27 changes: 27 additions & 0 deletions gateway/assets/dag.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,33 @@
<li><a href="?format=dag-cbor" rel="nofollow">Valid DAG-CBOR</a> (specs at <a href="https://ipld.io/specs/codecs/dag-cbor/spec/" target="_blank" rel="noopener noreferrer">IPLD</a> and <a href="https://www.iana.org/assignments/media-types/application/vnd.ipld.dag-cbor" target="_blank" rel="noopener noreferrer">IANA</a>)</li>
</ul>
</section>
{{ with .Node }}
<section class="full-width">
<header>
<strong>DAG Preview</strong>
</header>
{{ template "node" (args $ .) }}
</section>
{{ end }}
</main>
</body>
</html>

{{ define "node" }}
{{ $root := index . 0 }}
{{ $node := index . 1 }}
{{ if len $node.Values }}
<div class="grid dag">
{{ range $index, $key := $node.Keys }}
{{ template "node" (args $root $key) }}
{{ template "node" (args $root (index $node.Values $index)) }}
{{ end }}
</div>
{{ else }}
<div translate="no">
{{ with $node.CID }}<a class="ipfs-hash" href={{ if $root.DNSLink }}"https://cid.ipfs.tech/#{{ . | urlEscape}}" target="_blank" rel="noreferrer noopener"{{ else }}"{{ $root.GatewayURL }}/ipfs/{{ . | urlEscape}}"{{ end }}>{{ end }}
<code>{{- $node.Value -}}</code>
{{ with $node.CID }}</a>{{ end }}
</div>
{{ end }}
{{ end }}
110 changes: 110 additions & 0 deletions gateway/assets/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package assets

import (
"fmt"
"html/template"

"github.com/ipld/go-ipld-prime/datamodel"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
)

type ParsedNode struct {
Keys []*ParsedNode
Values []*ParsedNode
Value string
CID string
}

func ParseNode(node datamodel.Node) (*ParsedNode, error) {
dag := &ParsedNode{}

switch node.Kind() {
case datamodel.Kind_Map:
it := node.MapIterator()

for !it.Done() {
k, v, err := it.Next()
if err != nil {
return nil, err
}

kd, err := ParseNode(k)
if err != nil {
return nil, err
}

vd, err := ParseNode(v)
if err != nil {
return nil, err
}

dag.Keys = append(dag.Keys, kd)
dag.Values = append(dag.Values, vd)
}
case datamodel.Kind_List:
it := node.ListIterator()
for !it.Done() {
k, v, err := it.Next()
if err != nil {
return nil, err
}

vd, err := ParseNode(v)
if err != nil {
return nil, err
}

dag.Keys = append(dag.Keys, &ParsedNode{Value: fmt.Sprint(k)})
dag.Values = append(dag.Values, vd)
}
case datamodel.Kind_Bool:
v, err := node.AsBool()
if err != nil {
return nil, err
}
dag.Value = fmt.Sprintf("%t", v)
case datamodel.Kind_Int:
v, err := node.AsInt()
if err != nil {
return nil, err
}
dag.Value = fmt.Sprintf("%d", v)
case datamodel.Kind_Float:
v, err := node.AsFloat()
if err != nil {
return nil, err
}
dag.Value = fmt.Sprintf("%f", v)
case datamodel.Kind_String:
v, err := node.AsString()
if err != nil {
return nil, err
}
dag.Value = template.HTMLEscapeString(v)
case datamodel.Kind_Bytes:
v, err := node.AsBytes()
if err != nil {
return nil, err
}
dag.Value = fmt.Sprint(v)
case datamodel.Kind_Link:
lnk, err := node.AsLink()
if err != nil {
return nil, err
}
dag.Value = lnk.String()

cl, isCid := lnk.(cidlink.Link)
if isCid {
dag.CID = cl.Cid.String()
}
case datamodel.Kind_Invalid:
dag.Value = "INVALID"
case datamodel.Kind_Null:
dag.Value = "NULL"
default:
dag.Value = "UNKNOWN"
}

return dag, nil
}
36 changes: 32 additions & 4 deletions gateway/assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,17 @@ main section:not(:last-child) {
border-bottom: 1px solid var(--dark-white);
}

main section header {
background-color: var(--near-white);
}

.grid {
display: grid;
}

.grid > div {
padding: .7em;
border-top: 1px solid var(--dark-white);
border-bottom: 1px solid var(--dark-white);
}

.grid.dir {
Expand All @@ -165,8 +169,8 @@ main section:not(:last-child) {
padding-right: 1em;
}

.grid.dir > div:nth-child(-n+4) {
border-top: 0;
.grid.dir > div:nth-last-child(-n+4) {
border-bottom: 0;
}

.grid.dir > div:nth-of-type(8n+5),
Expand All @@ -176,6 +180,31 @@ main section:not(:last-child) {
background-color: var(--near-white);
}

.grid.dag {
grid-template-columns: max-content 1fr;
}

.grid.dag .grid {
padding: 0;
}

.grid.dag > div:nth-last-child(-n+2) {
border-bottom: 0;
}

.grid.dag > div {
background: white
}

.grid.dag > div:nth-child(4n),
.grid.dag > div:nth-child(4n+3) {
background-color: var(--near-white);
}

section > .grid.dag > div:nth-of-type(2n+1) {
padding-left: 1em;
}

.type-icon,
.type-icon > * {
width: 1.15em
Expand Down Expand Up @@ -222,4 +251,3 @@ main section:not(:last-child) {
display: none;
}
}

6 changes: 6 additions & 0 deletions gateway/assets/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ func iconFromExt(filename string) string {
return "ipfs-_blank" // Default is blank icon.
}

// args is a helper function to allow sending more than one object to a template.
func args(args ...interface{}) []interface{} {
return args
}

var funcMap = template.FuncMap{
"iconFromExt": iconFromExt,
"urlEscape": urlEscape,
"args": args,
}

func readFile(fs fs.FS, filename string) ([]byte, error) {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit d49baa8

Please sign in to comment.