Skip to content

Commit 67acbe0

Browse files
committed
feat: get rid of data uris for images in templates
1 parent 6641ba9 commit 67acbe0

File tree

8 files changed

+150
-9
lines changed

8 files changed

+150
-9
lines changed

frontend/package-lock.json

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"goober": "^2.1.14",
3737
"highlight.js": "^11.10.0",
3838
"ionicons": "^4.0.0",
39+
"jssha": "^3.3.1",
3940
"lodash-es": "^4.17.21",
4041
"markdown-it": "^12.3.2",
4142
"markdown-it-replace-link": "^1.2.1",

frontend/src/js/core/templating.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { cloneDeep } from 'lodash-es';
22

3+
import jsSHA from 'jssha';
34
import hash from 'object-hash';
45

56
import entry from 'js/types/entry';
@@ -211,6 +212,12 @@ export const render = (
211212
clonedState.aiToken = ai.value.token;
212213
}
213214

215+
Object.keys(clonedState.images).forEach((key) => {
216+
const imageHash = new jsSHA('SHA-256', 'TEXT', { encoding: 'UTF8' }).update(clonedState.images[key]).getHash('HEX');
217+
console.log(imageHash);
218+
clonedState.images[key] = `/api/image-cache/${imageHash}`;
219+
});
220+
214221
// Setup promises for response
215222
let id = hash + '-' + Math.ceil(Math.random() * 10000000).toString();
216223
workerPromises[id] = {

frontend/src/js/ui/components/editor/images.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,15 @@ export default (): m.Component<ImagesProps> => {
3737
className: '.mb3',
3838
onUpload: (name, image) => {
3939
if (!attrs.onChange) return;
40-
attrs.onChange({ ...attrs.images, [name]: image });
40+
41+
m.request({
42+
url: `/api/image-cache`,
43+
method: 'POST',
44+
body: image,
45+
}).then(() => {
46+
if (!attrs.onChange) return;
47+
attrs.onChange({ ...attrs.images, [name]: image });
48+
});
4149
},
4250
}),
4351
...map(attrs.images, (image, key) => {

frontend/src/js/ui/components/modals/session-grid/create-edit-grid-button.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import m from 'mithril';
22

33
import { GridElement, GridGeneratorElement, GridTemplateElement, isGridGeneratorElement, isGridTemplateElement } from 'js/types/session-grid';
44
import { buildId } from 'src/js/types/basic-info';
5-
import { is, safeCall, safePromise } from 'js/core/safe';
5+
import { safePromise } from 'js/core/safe';
66
import { getGridGeneratorConfigChoices, getGridTemplateChoices } from 'js/core/session-grid';
77
import { generators, templates } from 'js/core/store';
88

frontend/src/js/ui/views/template/create.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ import Template, { createEmptyTemplate } from 'js/types/template';
55
import * as API from 'js/core/api';
66
import store from 'js/core/store';
77

8+
import DividerVert from 'js/ui/shoelace/divider-vert';
89
import IconButton from 'js/ui/shoelace/icon-button';
910

11+
import Tooltip from 'js/ui/components/atomic/tooltip';
1012
import TemplateEditor from 'js/ui/components/editor/template';
13+
import Flex from 'js/ui/components/layout/flex';
14+
import { openDevTools } from 'js/ui/components/print-preview';
1115
import Base from 'js/ui/components/view-layout/base';
1216
import Breadcrumbs from 'js/ui/components/view-layout/breadcrumbs';
1317

@@ -40,7 +44,7 @@ export default (): m.Component<TemplateCreateProps> => {
4044
confirmText: 'Are you sure you want to leave this page? Changes are not saved.',
4145
items: [{ link: '/template', label: 'Templates' }, { label: 'Create Template' }],
4246
}),
43-
rightElement: [
47+
rightElement: m(Flex, { items: 'center' }, [
4448
m(
4549
IconButton,
4650
{
@@ -82,7 +86,21 @@ export default (): m.Component<TemplateCreateProps> => {
8286
},
8387
'Save',
8488
), //
85-
],
89+
m(DividerVert),
90+
m(
91+
Tooltip,
92+
{ content: 'Open Dev Tools' },
93+
m(IconButton, {
94+
className: '.mr2',
95+
intend: 'primary',
96+
icon: 'bug',
97+
size: 'sm',
98+
onClick: () => {
99+
openDevTools(document.body);
100+
},
101+
}),
102+
),
103+
]),
86104
active: 'templates',
87105
classNameContainer: '',
88106
},

rpc/image_utility.go

+100-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
package rpc
22

33
import (
4+
"crypto/sha256"
45
"errors"
5-
"github.com/BigJk/snd/rpc/bind"
6-
"github.com/labstack/echo/v4"
7-
"github.com/vincent-petithory/dataurl"
6+
"fmt"
7+
"io"
88
"io/ioutil"
99
"net/http"
1010
"strings"
11+
"sync"
12+
13+
"github.com/BigJk/snd/database"
14+
"github.com/BigJk/snd/rpc/bind"
15+
"github.com/labstack/echo/v4"
16+
"github.com/vincent-petithory/dataurl"
1117
)
1218

1319
// RegisterImageUtilities registers all image utilities.
14-
func RegisterImageUtilities(route *echo.Group) {
20+
func RegisterImageUtilities(route *echo.Group, db database.Database) {
1521
// fetchImage fetches a image from a url and returns it as a dataurl.
1622
bind.MustBind(route, "/fetchImage", func(url string) (string, error) {
1723
resp, err := http.Get(url)
@@ -30,4 +36,94 @@ func RegisterImageUtilities(route *echo.Group) {
3036

3137
return dataurl.EncodeBytes(data), nil
3238
})
39+
40+
//
41+
// Image cache to avoid using long data uris in the templates
42+
//
43+
44+
cacheMutex := sync.RWMutex{}
45+
cache := map[string]string{}
46+
47+
decodeDataURI := func(dataURI string) ([]byte, string, error) {
48+
data, err := dataurl.DecodeString(dataURI)
49+
if err != nil {
50+
return nil, "", err
51+
}
52+
contentType := http.DetectContentType(data.Data)
53+
return data.Data, contentType, nil
54+
}
55+
56+
// Fill cache with templates
57+
templates, err := db.GetTemplates()
58+
if err == nil {
59+
for _, template := range templates {
60+
for _, img := range template.Images {
61+
hash := sha256.Sum256([]byte(img))
62+
hashHex := fmt.Sprintf("%x", hash)
63+
64+
cacheMutex.Lock()
65+
cache[hashHex] = img
66+
cacheMutex.Unlock()
67+
}
68+
}
69+
}
70+
71+
route.POST("/image-cache", func(c echo.Context) error {
72+
body, err := io.ReadAll(c.Request().Body)
73+
if err != nil {
74+
return c.JSON(http.StatusBadRequest, err)
75+
}
76+
77+
dataUri := strings.Trim(string(body), "\"")
78+
hash := sha256.Sum256([]byte(dataUri))
79+
hashHex := fmt.Sprintf("%x", hash)
80+
81+
cacheMutex.Lock()
82+
cache[hashHex] = dataUri
83+
cacheMutex.Unlock()
84+
85+
return c.NoContent(http.StatusOK)
86+
})
87+
88+
route.GET("/image-cache/:id", func(c echo.Context) error {
89+
hash := c.Param("id")
90+
91+
cacheMutex.RLock()
92+
val, ok := cache[hash]
93+
cacheMutex.RUnlock()
94+
95+
if ok {
96+
data, contentType, err := decodeDataURI(val)
97+
if err != nil {
98+
return c.JSON(http.StatusBadRequest, err)
99+
}
100+
return c.Blob(http.StatusOK, contentType, data)
101+
}
102+
103+
templates, err := db.GetTemplates()
104+
if err != nil {
105+
return c.JSON(http.StatusBadRequest, err)
106+
}
107+
108+
for _, template := range templates {
109+
for _, img := range template.Images {
110+
hash := sha256.Sum256([]byte(img))
111+
hashHex := fmt.Sprintf("%x", hash)
112+
113+
cacheMutex.Lock()
114+
cache[hashHex] = img
115+
cacheMutex.Unlock()
116+
117+
if hashHex == c.Param("id") {
118+
data, contentType, err := decodeDataURI(img)
119+
if err != nil {
120+
return c.JSON(http.StatusBadRequest, err)
121+
}
122+
return c.Blob(http.StatusOK, contentType, data)
123+
}
124+
}
125+
}
126+
127+
return c.JSON(http.StatusBadRequest, errors.New("image not found"))
128+
})
33129
}

server/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func (s *Server) Start(bindAddr string) error {
163163

164164
rpc.RegisterVersion(api)
165165
rpc.RegisterKeyValue(api, s.db)
166-
rpc.RegisterImageUtilities(api)
166+
rpc.RegisterImageUtilities(api, s.db)
167167
rpc.RegisterSettings(api, s.db)
168168
rpc.RegisterTemplate(api, extern, s.db)
169169
rpc.RegisterGenerator(api, extern, s.db)

0 commit comments

Comments
 (0)