You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
8.7 KiB

  1. const creatures = [
  2. ]
  3. const outputHeaderElm = document.getElementById("output-header")
  4. const outputElm = document.getElementById("output")
  5. const statusElm = document.getElementById("status")
  6. const dbnameField = document.getElementById("dbname")
  7. const dbAddressField = document.getElementById("dbaddress")
  8. const createButton = document.getElementById("create")
  9. const openButton = document.getElementById("open")
  10. const createType = document.getElementById("type")
  11. const writerText = document.getElementById("writerText")
  12. const publicCheckbox = document.getElementById("public")
  13. const readonlyCheckbox = document.getElementById("readonly")
  14. function handleError(e) {
  15. console.error(e.stack)
  16. statusElm.innerHTML = e.message
  17. }
  18. const main = async (IPFS, ORBITDB) => {
  19. let orbitdb, db
  20. let count = 0
  21. let interval = Math.floor((Math.random() * 300) + (Math.random() * 2000))
  22. let updateInterval
  23. let dbType, dbAddress
  24. // If we're building with Webpack, use the injected IPFS module.
  25. // Otherwise use 'Ipfs' which is exposed by ipfs.min.js
  26. if (IPFS)
  27. Ipfs = IPFS
  28. // If we're building with Webpack, use the injected OrbitDB module.
  29. // Otherwise use 'OrbitDB' which is exposed by orbitdb.min.js
  30. if (ORBITDB)
  31. OrbitDB = ORBITDB
  32. // Init UI
  33. openButton.disabled = true
  34. createButton.disabled = true
  35. statusElm.innerHTML = "Starting IPFS..."
  36. // Create IPFS instance
  37. const ipfs = await Ipfs.create({
  38. repo: '/orbitdb/examples/browser/new/ipfs/0.33.1',
  39. start: true,
  40. preload: {
  41. enabled: false
  42. },
  43. EXPERIMENTAL: {
  44. pubsub: true,
  45. },
  46. config: {
  47. Addresses: {
  48. Swarm: [
  49. // Use IPFS dev signal server
  50. // '/dns4/star-signal.cloud.ipfs.team/wss/p2p-webrtc-star',
  51. // '/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star',
  52. // Use IPFS dev webrtc signal server
  53. '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star/',
  54. '/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star/',
  55. '/dns4/webrtc-star.discovery.libp2p.io/tcp/443/wss/p2p-webrtc-star/',
  56. // Use local signal server
  57. // '/ip4/0.0.0.0/tcp/9090/wss/p2p-webrtc-star',
  58. ]
  59. },
  60. }
  61. })
  62. openButton.disabled = false
  63. createButton.disabled = false
  64. statusElm.innerHTML = "IPFS Started"
  65. orbitdb = await OrbitDB.createInstance(ipfs)
  66. const load = async (db, statusText) => {
  67. // Set the status text
  68. statusElm.innerHTML = statusText
  69. // When the database is ready (ie. loaded), display results
  70. db.events.on('ready', () => queryAndRender(db))
  71. // When database gets replicated with a peer, display results
  72. db.events.on('replicated', () => queryAndRender(db))
  73. // When we update the database, display result
  74. db.events.on('write', () => queryAndRender(db))
  75. db.events.on('replicate.progress', () => queryAndRender(db))
  76. // Hook up to the load progress event and render the progress
  77. let maxTotal = 0, loaded = 0
  78. db.events.on('load.progress', (address, hash, entry, progress, total) => {
  79. loaded ++
  80. maxTotal = Math.max.apply(null, [maxTotal, progress, 0])
  81. total = Math.max.apply(null, [progress, maxTotal, total, entry.clock.time, 0])
  82. statusElm.innerHTML = `Loading database... ${maxTotal} / ${total}`
  83. })
  84. db.events.on('ready', () => {
  85. // Set the status text
  86. setTimeout(() => {
  87. statusElm.innerHTML = 'Database is ready'
  88. }, 1000)
  89. })
  90. // Load locally persisted database
  91. await db.load()
  92. }
  93. const startWriter = async (db, interval) => {
  94. // Set the status text
  95. writerText.innerHTML = `Writing to database every ${interval} milliseconds...`
  96. // Start update/insert loop
  97. updateInterval = setInterval(async () => {
  98. try {
  99. await update(db)
  100. } catch (e) {
  101. console.error(e.toString())
  102. writerText.innerHTML = '<span style="color: red">' + e.toString() + '</span>'
  103. clearInterval(updateInterval)
  104. }
  105. }, interval)
  106. }
  107. const resetDatabase = async (db) => {
  108. writerText.innerHTML = ""
  109. outputElm.innerHTML = ""
  110. outputHeaderElm.innerHTML = ""
  111. clearInterval(updateInterval)
  112. if (db) {
  113. await db.close()
  114. }
  115. interval = Math.floor((Math.random() * 300) + (Math.random() * 2000))
  116. }
  117. const createDatabase = async () => {
  118. await resetDatabase(db)
  119. openButton.disabled = true
  120. createButton.disabled = true
  121. try {
  122. const name = dbnameField.value
  123. const type = createType.value
  124. const publicAccess = publicCheckbox.checked
  125. db = await orbitdb.open(name, {
  126. // If database doesn't exist, create it
  127. create: true,
  128. overwrite: true,
  129. // Load only the local version of the database,
  130. // don't load the latest from the network yet
  131. localOnly: false,
  132. type: type,
  133. // If "Public" flag is set, allow anyone to write to the database,
  134. // otherwise only the creator of the database can write
  135. accessController: {
  136. write: publicAccess ? ['*'] : [orbitdb.identity.id],
  137. }
  138. })
  139. await load(db, 'Creating database...')
  140. startWriter(db, interval)
  141. } catch (e) {
  142. console.error(e)
  143. }
  144. openButton.disabled = false
  145. createButton.disabled = false
  146. }
  147. const openDatabase = async () => {
  148. const address = dbAddressField.value
  149. await resetDatabase(db)
  150. openButton.disabled = true
  151. createButton.disabled = true
  152. try {
  153. statusElm.innerHTML = "Connecting to peers..."
  154. db = await orbitdb.open(address, { sync: true })
  155. await load(db, 'Loading database...')
  156. if (!readonlyCheckbox.checked) {
  157. startWriter(db, interval)
  158. } else {
  159. writerText.innerHTML = `Listening for updates to the database...`
  160. }
  161. } catch (e) {
  162. console.error(e)
  163. }
  164. openButton.disabled = false
  165. createButton.disabled = false
  166. }
  167. const update = async (db) => {
  168. count ++
  169. const time = new Date().toISOString()
  170. const idx = Math.floor(Math.random() * creatures.length)
  171. const creature = creatures[idx]
  172. if (db.type === 'eventlog') {
  173. const value = "GrEEtinGs from " + orbitdb.id + " " + creature + ": Hello #" + count + " (" + time + ")"
  174. await db.add(value)
  175. } else if (db.type === 'feed') {
  176. const value = "GrEEtinGs from " + orbitdb.id + " " + creature + ": Hello #" + count + " (" + time + ")"
  177. await db.add(value)
  178. } else if (db.type === 'docstore') {
  179. const value = { _id: 'peer1', avatar: creature, updated: time }
  180. await db.put(value)
  181. } else if (db.type === 'keyvalue') {
  182. await db.set('mykey', creature)
  183. } else if (db.type === 'counter') {
  184. await db.inc(1)
  185. } else {
  186. throw new Error("Unknown datatbase type: ", db.type)
  187. }
  188. }
  189. const query = (db) => {
  190. if (db.type === 'eventlog')
  191. return db.iterator({ limit: 5 }).collect()
  192. else if (db.type === 'feed')
  193. return db.iterator({ limit: 5 }).collect()
  194. else if (db.type === 'docstore')
  195. return db.get('peer1')
  196. else if (db.type === 'keyvalue')
  197. return db.get('mykey')
  198. else if (db.type === 'counter')
  199. return db.value
  200. else
  201. throw new Error("Unknown datatbase type: ", db.type)
  202. }
  203. const queryAndRender = async (db) => {
  204. const networkPeers = await ipfs.swarm.peers()
  205. const databasePeers = await ipfs.pubsub.peers(db.address.toString())
  206. const result = query(db)
  207. if (dbType !== db.type || dbAddress !== db.address) {
  208. dbType = db.type;
  209. dbAddress = db.address;
  210. outputHeaderElm.innerHTML = `
  211. <h2>${dbType.toUpperCase()}</h2>
  212. <h3 id="remoteAddress">${dbAddress}</h3>
  213. <p>Copy this address and use the 'Open Remote Database' in another browser to replicate this database between peers.</p>
  214. `
  215. }
  216. outputElm.innerHTML = `
  217. <div><b>Peer ID:</b> ${orbitdb.id}</div>
  218. <div><b>Peers (database/network):</b> ${databasePeers.length} / ${networkPeers.length}</div>
  219. <div><b>Oplog Size:</b> ${Math.max(db._replicationStatus.progress, db._oplog.length)} / ${db._replicationStatus.max}</div>
  220. <h2>Results</h2>
  221. <div id="results">
  222. <div>
  223. ${result && Array.isArray(result) && result.length > 0 && db.type !== 'docstore' && db.type !== 'keyvalue'
  224. ? result.slice().reverse().map((e) => e.payload.value).join('<br>\n')
  225. : db.type === 'docstore'
  226. ? JSON.stringify(result, null, 2)
  227. : result ? result.toString().replace('"', '').replace('"', '') : result
  228. }
  229. </div>
  230. </div>
  231. `
  232. }
  233. openButton.addEventListener('click', openDatabase)
  234. createButton.addEventListener('click', createDatabase)
  235. }
  236. if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
  237. module.exports = main