1//
2// User
3// (づ。◕‿‿◕。)づ
4//
5// Related to the user layer.
6
7
8// @ts-ignore
9import * as TaskPort from "elm-taskport"
10
11import type { App } from "./elm/types"
12
13import * as crypto from "../crypto"
14
15import { decryptIfNeeded, encryptIfPossible, SECRET_KEY_LOCATION } from "./common"
16import { parseJsonIfNeeded, toCache } from "./common"
17
18
19const ports: Record<string, any> = {}
20const taskPorts: Record<string, any> = {}
21
22
23// Crypto
24// ======
25
26taskPorts.fabricateSecretKey = async passphrase => {
27 const data = await crypto.keyFromPassphrase(passphrase)
28 return toCache(SECRET_KEY_LOCATION, data)
29}
30
31
32
33// Dropbox
34// -------
35
36taskPorts.fromDropbox = ({ fileName, token }) => {
37 return fetch("https://content.dropboxapi.com/2/files/download", {
38 method: "POST",
39 headers: {
40 "Authorization": "Bearer " + token,
41 "Dropbox-API-Arg": JSON.stringify({ path: "/" + fileName })
42 }
43 })
44 .then(r => r.ok ? r.text() : r.json())
45 .then(r => r.error ? null : r)
46 .then(decryptIfNeeded)
47 .then(parseJsonIfNeeded)
48}
49
50
51taskPorts.toDropbox = async ({ fileName, data, token }) => {
52 const json = JSON.stringify(data)
53 const params = {
54 path: "/" + fileName,
55 mode: "overwrite",
56 mute: true
57 }
58
59 return fetch("https://content.dropboxapi.com/2/files/upload", {
60 method: "POST",
61 headers: {
62 "Authorization": "Bearer " + token,
63 "Content-Type": "application/octet-stream",
64 "Dropbox-API-Arg": JSON.stringify(params)
65 },
66 body: await encryptIfPossible(json)
67 })
68}
69
70
71
72// IPFS
73// ----
74
75const IPFS_ROOT = "/Applications/Diffuse/"
76
77
78taskPorts.fromIpfs = ({ apiOrigin, fileName }) => {
79 const path = IPFS_ROOT + fileName
80
81 return fetch(apiOrigin + "/api/v0/files/read?arg=" + encodeURIComponent(path), { method: "POST" })
82 .then(r => r.ok ? r.text() : r.json())
83 .then(r => r.Code === 0 ? null : r)
84 .then(decryptIfNeeded)
85 .then(parseJsonIfNeeded)
86}
87
88
89taskPorts.toIpfs = ({ apiOrigin, fileName, data }) => {
90 const json = JSON.stringify(data)
91 const params = new URLSearchParams({
92 arg: IPFS_ROOT + fileName,
93 create: "true",
94 offset: "0",
95 parents: "true",
96 truncate: "true"
97 }).toString()
98
99 return encryptIfPossible(json).then(possiblyEncryptedData => {
100 const formData = new FormData()
101
102 formData.append("data", possiblyEncryptedData)
103
104 return fetch(
105 apiOrigin + "/api/v0/files/write?" + params,
106 { method: "POST", body: formData }
107 )
108 })
109}
110
111
112
113// Remote Storage
114// --------------
115
116let rs
117let rsClient
118
119
120async function remoteStorage(userAddress: string, token: string) {
121 if (!rs) {
122 const { default: RemoteStorage } = await import("remotestoragejs")
123
124 rs = new RemoteStorage({ cache: false })
125 rs.access.claim("diffuse", "rw")
126
127 rsClient = rs.scope("/diffuse/")
128
129 return new Promise(resolve => {
130 rs.on("connected", resolve)
131 rs.connect(userAddress, token)
132 })
133
134 } else {
135 return Promise.resolve()
136
137 }
138}
139
140
141ports.deconstructRemoteStorage = _app => _ => {
142 rs = null
143 rsClient = null
144}
145
146
147taskPorts.fromRemoteStorage = ({ fileName, userAddress, token }) => {
148 return remoteStorage(userAddress, token)
149 .then(_ => rsClient.getFile(fileName))
150 .then(r => r.data)
151 .then(decryptIfNeeded)
152 .then(parseJsonIfNeeded)
153}
154
155
156taskPorts.toRemoteStorage = ({ data, fileName, userAddress, token }) => {
157 const json = JSON.stringify(data)
158
159 return remoteStorage(userAddress, token)
160 .then(_ => encryptIfPossible(json))
161 .then(data => rsClient.storeFile("application/json", fileName, data))
162}
163
164
165
166// EXPORT
167// ======
168
169function registerPorts(app: App) {
170 Object.keys(ports).forEach(name => {
171 const fn = ports[ name ](app)
172 app.ports[ name ].subscribe(fn)
173 })
174}
175
176function registerTaskPorts() {
177 Object.keys(taskPorts).forEach(name => {
178 const fn = taskPorts[ name ]
179 TaskPort.register(name, fn)
180 })
181}
182
183
184export const TaskPorts = { register: registerTaskPorts }
185export const Ports = { register: registerPorts }