create-blocklet 0.3.0 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/index.js +33 -14
  2. package/lib/arcblock.js +8 -6
  3. package/lib/git.js +10 -3
  4. package/lib/index.js +4 -4
  5. package/lib/npm.js +10 -3
  6. package/lib/server.js +16 -0
  7. package/package.json +1 -1
  8. package/template-api/express/README.md +146 -0
  9. package/template-api/express/api/hooks/pre-start.js +33 -0
  10. package/template-api/express/api/index.js +13 -0
  11. package/template-api/express/api/libs/auth.js +22 -0
  12. package/template-api/express/api/libs/env.js +6 -0
  13. package/template-api/express/api/libs/logger.js +3 -0
  14. package/template-api/express/blocklet.md +3 -0
  15. package/template-api/express/blocklet.yml +57 -0
  16. package/template-api/express/package.json +26 -0
  17. package/template-api/express/screenshots/.gitkeep +0 -0
  18. package/template-dapp/nextjs/.eslintrc.js +4 -0
  19. package/template-dapp/nextjs/README.md +153 -0
  20. package/template-dapp/nextjs/api/hooks/pre-start.js +33 -0
  21. package/template-dapp/nextjs/api/index.js +24 -0
  22. package/template-dapp/nextjs/api/libs/auth.js +22 -0
  23. package/template-dapp/nextjs/api/libs/env.js +6 -0
  24. package/template-dapp/nextjs/api/libs/logger.js +3 -0
  25. package/template-dapp/nextjs/api/routes/index.js +6 -0
  26. package/template-dapp/nextjs/blocklet.md +3 -0
  27. package/template-dapp/nextjs/blocklet.yml +58 -0
  28. package/template-dapp/nextjs/next.config.js +6 -0
  29. package/template-dapp/nextjs/package.json +48 -0
  30. package/template-dapp/nextjs/pages/_app.js +21 -0
  31. package/template-dapp/nextjs/pages/api/hello.js +5 -0
  32. package/template-dapp/nextjs/pages/index.js +69 -0
  33. package/template-dapp/nextjs/styles/Home.module.css +116 -0
  34. package/template-dapp/nextjs/styles/globals.css +16 -0
  35. package/template-dapp/react/blocklet.yml +1 -1
  36. package/template-dapp/vue/blocklet.yml +1 -1
  37. package/template-dapp/vue/package.json +2 -1
  38. package/template-dapp/vue/vite.config.js +2 -0
  39. package/template-dapp/vue2/blocklet.yml +1 -1
  40. package/template-static/vue/package.json +2 -1
  41. package/template-static/vue/vite.config.js +2 -0
package/index.js CHANGED
@@ -41,6 +41,11 @@ const TYPES = [
41
41
  display: 'vue2 + @vue/cli',
42
42
  color: green,
43
43
  },
44
+ {
45
+ name: 'nextjs',
46
+ display: 'next.js',
47
+ color: blue,
48
+ },
44
49
  ],
45
50
  },
46
51
  {
@@ -69,6 +74,17 @@ const TYPES = [
69
74
  },
70
75
  ],
71
76
  },
77
+ {
78
+ name: 'api',
79
+ color: yellow,
80
+ frameworks: [
81
+ {
82
+ name: 'express',
83
+ display: 'express',
84
+ color: yellow,
85
+ },
86
+ ],
87
+ },
72
88
  ];
73
89
 
74
90
  const renameFiles = {
@@ -81,14 +97,7 @@ const renameFiles = {
81
97
  async function init() {
82
98
  const { version } = await fs.readJSONSync(path.resolve(__dirname, 'package.json'));
83
99
 
84
- console.log(
85
- boxen(`${bold('Blocklet') + dim(' Starter')} ${blue(`v${version}`)}`, {
86
- padding: 1,
87
- margin: 1,
88
- float: 'center',
89
- borderStyle: 'double',
90
- })
91
- );
100
+ await echoBrand({ version });
92
101
 
93
102
  let targetDir = argv._[0] ? String(argv._[0]) : undefined;
94
103
 
@@ -142,6 +151,7 @@ async function init() {
142
151
  const TYPE_TITLE = {
143
152
  dapp: 'fullstack: webapp with backend code',
144
153
  static: 'webapp: browser only',
154
+ api: 'api: backend only',
145
155
  };
146
156
  return {
147
157
  title: TYPE_TITLE[type.name],
@@ -172,12 +182,14 @@ async function init() {
172
182
  name: 'authorName',
173
183
  message: 'Author name:',
174
184
  initial: authorInfo?.name || '',
185
+ validate: (name) => (name ? true : 'Author name is required'),
175
186
  },
176
187
  {
177
188
  type: 'text',
178
189
  name: 'authorEmail',
179
190
  message: 'Author email:',
180
191
  initial: authorInfo?.email || '',
192
+ validate: (email) => (email ? true : 'Author email is required'),
181
193
  },
182
194
  ],
183
195
  {
@@ -204,7 +216,6 @@ async function init() {
204
216
  fs.mkdirSync(root);
205
217
  }
206
218
 
207
- await echoBrand();
208
219
  await echoDocument();
209
220
 
210
221
  console.log('Checking blocklet server runtime environment...', '\n');
@@ -231,6 +242,10 @@ async function init() {
231
242
  // eslint-disable-next-line no-continue
232
243
  continue;
233
244
  }
245
+ if (framework === 'express' && ['_eslintignore', '.husky'].includes(file)) {
246
+ // eslint-disable-next-line no-continue
247
+ continue;
248
+ }
234
249
  const targetPath = renameFiles[file] ? path.join(root, renameFiles[file]) : path.join(root, file);
235
250
  copy(path.join(commonDir, file), targetPath);
236
251
  }
@@ -335,7 +350,7 @@ async function init() {
335
350
  const { agent } = await prompts({
336
351
  name: 'agent',
337
352
  type: 'select',
338
- message: 'Choose the agent',
353
+ message: 'Select npm client (package manager)',
339
354
  choices: agentList.map((i) => ({ value: i, title: i })),
340
355
  });
341
356
 
@@ -421,7 +436,10 @@ async function init() {
421
436
  }
422
437
  function read(file) {
423
438
  const targetPath = path.join(root, file);
424
- return fs.readFileSync(targetPath, 'utf8');
439
+ if (fs.existsSync(targetPath)) {
440
+ return fs.readFileSync(targetPath, 'utf8');
441
+ }
442
+ return null;
425
443
  }
426
444
 
427
445
  function modifyPackage(modifyFn = () => {}) {
@@ -442,11 +460,12 @@ async function init() {
442
460
  write('blocklet.md', modifyMd);
443
461
  }
444
462
  function modifyEnv(modifyFn = (...args) => ({ ...args })) {
445
- try {
446
- const env = envfile.parse(read('.env'));
463
+ const envContent = read('.env');
464
+ if (envContent) {
465
+ const env = envfile.parse(envContent);
447
466
  modifyFn(env);
448
467
  write('.env', envfile.stringify(env));
449
- } catch {
468
+ } else {
450
469
  console.warn(`\n${yellow('No .env file found, please add one.')}`);
451
470
  }
452
471
  }
package/lib/arcblock.js CHANGED
@@ -5,7 +5,7 @@ import { chalk } from 'zx';
5
5
 
6
6
  const { green } = chalk;
7
7
 
8
- export function echoBrand() {
8
+ export function echoBrand({ version = '' }) {
9
9
  let msg;
10
10
  return new Promise((resolve, reject) => {
11
11
  figlet.text('ArcBlock', { width: 44 }, (err, data) => {
@@ -14,9 +14,11 @@ export function echoBrand() {
14
14
  }
15
15
  const symbolLen = 44;
16
16
  const indent = (symbolLen - 10) / 2;
17
- msg = gradient(['cyan', 'rgb(0, 111, 150)', 'rgb(0, 246,136)']).multiline(
18
- ['', `${' '.repeat(indent)}Powered By`, data].join('\n')
19
- );
17
+ const msgList = [`\n${' '.repeat(indent)}Powered By`, data];
18
+ if (version) {
19
+ msgList.push(`${' '.repeat((symbolLen - 20) / 2)}Create Blocklet v${version}\n`);
20
+ }
21
+ msg = gradient(['cyan', 'rgb(0, 111, 150)', 'rgb(0, 246,136)']).multiline(msgList.join('\n'));
20
22
  console.log(msg);
21
23
  return resolve(msg);
22
24
  });
@@ -28,8 +30,8 @@ export function echoDocument() {
28
30
  if (terminalLink.isSupported) {
29
31
  msg = green(terminalLink(`Documentation: ${url}`, url));
30
32
  } else {
31
- msg = green(`Documentation: ${url}`);
33
+ msg = green(`Check documentation in here: ${url}`);
32
34
  }
33
- console.log(msg, '\n');
35
+ console.log('\n', msg, '\n');
34
36
  return msg;
35
37
  }
package/lib/git.js CHANGED
@@ -19,7 +19,14 @@ export async function initGitRepo(root) {
19
19
  }
20
20
 
21
21
  export async function getUserInfo() {
22
- const { stdout: name } = await $`git config user.name`;
23
- const { stdout: email } = await $`git config user.email`;
24
- return { name: name.trim(), email: email.trim() };
22
+ try {
23
+ const { stdout: name } = await $`git config user.name`;
24
+ const { stdout: email } = await $`git config user.email`;
25
+ return { name: name.trim(), email: email.trim() };
26
+ } catch {
27
+ return {
28
+ name: '',
29
+ email: '',
30
+ };
31
+ }
25
32
  }
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { $ } from 'zx';
2
2
  import { getAuthor } from './npm.js';
3
3
  import { getUserInfo } from './git.js';
4
+ import { getUserInfo as getServerUserInfo } from './server.js';
4
5
 
5
6
  $.verbose = false;
6
7
 
@@ -10,10 +11,9 @@ export async function getOutput(cmd) {
10
11
  }
11
12
 
12
13
  export async function getUser() {
13
- const npmAuthor = await getAuthor();
14
- const gitUser = await getUserInfo();
14
+ const [npmAuthor, gitUser, serverUser] = await Promise.all([getAuthor(), getUserInfo(), getServerUserInfo()]);
15
15
  return {
16
- name: npmAuthor.name || gitUser.name,
17
- email: npmAuthor.email || gitUser.email,
16
+ name: serverUser.name || npmAuthor.name || gitUser.name || '',
17
+ email: serverUser.email || npmAuthor.email || gitUser.email || '',
18
18
  };
19
19
  }
package/lib/npm.js CHANGED
@@ -4,8 +4,15 @@ import { $ } from 'zx';
4
4
  $.verbose = false;
5
5
 
6
6
  export async function getAuthor() {
7
- const { stdout: name } = await $`npm config get init.author.name`;
8
- const { stdout: email } = await $`npm config get init.author.email`;
7
+ try {
8
+ const { stdout: name } = await $`npm config get init.author.name`;
9
+ const { stdout: email } = await $`npm config get init.author.email`;
9
10
 
10
- return { name: name.trim(), email: email.trim() };
11
+ return { name: name.trim(), email: email.trim() };
12
+ } catch {
13
+ return {
14
+ name: '',
15
+ email: '',
16
+ };
17
+ }
11
18
  }
package/lib/server.js CHANGED
@@ -45,3 +45,19 @@ export async function getServerDirectory() {
45
45
  const directory = matchStr.replace(/Blocklet Server Data Directory:[\s]*([\S]+)\/\.abtnode\n/gm, '$1');
46
46
  return directory;
47
47
  }
48
+
49
+ export async function getUserInfo() {
50
+ try {
51
+ const { stdout: user } = await $`blocklet config get user`;
52
+ const { stdout: email } = await $`blocklet config get email`;
53
+ return {
54
+ user: user?.trim(),
55
+ email: email?.trim(),
56
+ };
57
+ } catch {
58
+ return {
59
+ user: '',
60
+ email: '',
61
+ };
62
+ }
63
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-blocklet",
3
- "version": "0.3.0",
3
+ "version": "0.3.3",
4
4
  "exports": "./index.js",
5
5
  "type": "module",
6
6
  "repository": "git@github.com:blocklet/create-blocklet.git",
@@ -0,0 +1,146 @@
1
+ # Getting Started with Create Blocklet
2
+
3
+ This project was bootstrapped with [Create Blocklet](https://github.com/blocklet/create-blocklet).
4
+
5
+ This blocklet is a dapp project, which means this is a full-stack application. It's contained both `server` and `client` code.
6
+
7
+ ## File Structure
8
+
9
+ - screenshots/ - Screenshots
10
+ - api/ - Api side code
11
+ - hooks/ - blocklet lifecycle hooks
12
+ - libs/ - Api side libraries
13
+ - middlewares/ - Api side middlewares
14
+ - routes/ - Api side routes
15
+ - index.js - Api side entry point
16
+ - .env - Environment variables
17
+ - .env.local - Local environment variables
18
+ - .prettierrc - Prettier configuration
19
+ - blocklet.md - Blocklet README
20
+ - blocklet.yml - Blocklet configuration
21
+ - LICENSE - License file
22
+ - logo.png - Blocklet logo file
23
+ - Makefile - Makefile
24
+ - package.json - Npm package file
25
+ - README.md - A guide for this blocklet
26
+ - version - Version file
27
+
28
+ ## Development
29
+
30
+ 1. Make sure you have [@blocklet/cli](https://www.npmjs.com/package/@blocklet/cli) installed
31
+
32
+ Blocklet needs blocklet server as a dependency. So you need to install it first.
33
+ `npm install -g @blocklet/cli`
34
+ See details in [https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#use-the-binary-distribution](https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#use-the-binary-distribution)
35
+
36
+ 2. Init blocklet server & start blocklet server
37
+
38
+ Before starting an blocklet server, you need to init blocklet server.
39
+ `blocklet server init --mode=debug`
40
+ `blocklet server start`
41
+ See details in [https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#configure-abt-node](https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#configure-abt-node)
42
+
43
+ 3. Go to the project directory `cd [name]`
44
+ 4. Install dependencies: `npm install` or `yarn`
45
+ 5. Start development server: `blocklet dev`
46
+
47
+ ## Bundle
48
+
49
+ After developing a blocklet, you may need to bundle it. Use `npm run bundle` command.
50
+
51
+ ## Deploy
52
+
53
+ - If you want to deploy this blocklet to local blocklet server, you can use `blocklet deploy .blocklet/bundle` command(Make sure the blocklet is bundled before deployment).
54
+ > Or you can simply use `npm run deploy` command.
55
+ - If you want to deploy this blocklet to remote blocklet server, you can use the command below.
56
+
57
+ ```shell
58
+ blocklet deploy .blocklet/bundle --endpoint {your blocklet server url} --access-key {blocklet server access key} --access-secret {blocklet server access secret}
59
+ ```
60
+
61
+ > Make sure the blocklet is bundled before deployment.
62
+
63
+ ## Upload to blocklet store
64
+
65
+ - If you want to upload the blocklet to any store for other users to download and use, you can following the following instructions.
66
+
67
+ Bump version at first.
68
+
69
+ ```shell
70
+ make bump-version
71
+ ```
72
+
73
+ Then config blocklet store url.
74
+ You can use those store url in below.
75
+
76
+ 1. [https://store.blocklet.dev/](https://store.blocklet.dev/)
77
+ 2. [https://dev.store.blocklet.dev/](https://dev.store.blocklet.dev/)
78
+ 3. A blocklet store started by yourself.
79
+ > Make sure you have installed a `blocklet store` on your own blocklet server. Check it on here: [https://store.blocklet.dev/blocklet/z8ia29UsENBg6tLZUKi2HABj38Cw1LmHZocbQ](https://store.blocklet.dev/blocklet/z8ia29UsENBg6tLZUKi2HABj38Cw1LmHZocbQ)
80
+
81
+ ```shell
82
+ blocklet config set store {store url}
83
+ ```
84
+
85
+ Get a `accessToken` by using this command.
86
+
87
+ > Why we need a `accessToken`?
88
+ > A `accessToken` is genrate by blocklet store, which help us upload our blocklet to any store.
89
+
90
+ Set `accessToken` to blocklet config
91
+
92
+ ```shell
93
+ blocklet config set accessToken {accessToken}
94
+ ```
95
+
96
+ Upload a new version to a store.
97
+
98
+ > Make sure the blocklet is bundled before upload.
99
+
100
+ ```shell
101
+ blocklet upload
102
+ ```
103
+
104
+ Or you can simply use `npm run upload` command.
105
+
106
+ - You also can upload a new version to a store by Github CI.
107
+ Bump version at first.
108
+
109
+ ```shell
110
+ make bump-version
111
+ ```
112
+
113
+ Push your code to Github main/master branch, or make a pull request to the main/master branch.
114
+ The CI workflow will automatically upload a new version to a store.
115
+
116
+ ## Q & A
117
+
118
+ 1. Q: How to change a blocklet's name?
119
+
120
+ A: Change the `name` field in the `package.json` file, change the `name` field in the `blocklet.yml` file.
121
+
122
+ You can also change the `title` field and `description` field in the `blocklet.yml` file.
123
+
124
+ Run `blocklet meta` command, you will get a `did` config, copy the `did` value.
125
+
126
+ Replace this command `"bundle:client": "PUBLIC_URL='/.blocklet/proxy/{did}' npm run build",` in `package.json`
127
+
128
+ Replace `did` field in the `blocklet.yml`
129
+
130
+ 2. Q: How to change a blocklet's logo?
131
+
132
+ Change the `logo.png` file root folder.
133
+
134
+ Or you can change the `logo` field in the `blocklet.yml` file.
135
+
136
+ > Make sure you have added the logo path to the `blocklet.yml` file `files` field.
137
+
138
+ ## Learn More
139
+
140
+ - Full specification of `blocklet.yml`: [https://github.com/blocklet/blocklet-specification/blob/main/docs/meta.md](https://github.com/blocklet/blocklet-specification/blob/main/docs/meta.md)
141
+ - Full document of Blocklet Server & blocklet development: [https://docs.arcblock.io/abtnode/en/introduction](https://docs.arcblock.io/abtnode/en/introduction)
142
+
143
+ ## License
144
+
145
+ The code is licensed under the Apache 2.0 license found in the
146
+ [LICENSE](LICENSE) file.
@@ -0,0 +1,33 @@
1
+ require('@blocklet/sdk/lib/error-handler');
2
+ require('dotenv-flow').config();
3
+
4
+ const Client = require('@ocap/client');
5
+
6
+ const env = require('../libs/env');
7
+ const logger = require('../libs/logger');
8
+ const { wallet } = require('../libs/auth');
9
+ const { name } = require('../../package.json');
10
+
11
+ const ensureAccountDeclared = async () => {
12
+ if (env.isComponent) return;
13
+ if (!env.chainHost) return;
14
+
15
+ const client = new Client(env.chainHost);
16
+ const { state } = await client.getAccountState({ address: wallet.toAddress() }, { ignoreFields: ['context'] });
17
+ if (!state) {
18
+ const hash = await client.declare({ moniker: name, wallet });
19
+ logger.log(`app account declared on chain ${env.chainHost}`, hash);
20
+ } else {
21
+ logger.log(`app account already declared on chain ${env.chainHost}`);
22
+ }
23
+ };
24
+
25
+ (async () => {
26
+ try {
27
+ await ensureAccountDeclared();
28
+ process.exit(0);
29
+ } catch (err) {
30
+ logger.error(`${name} pre-start error`, err.message);
31
+ process.exit(1);
32
+ }
33
+ })();
@@ -0,0 +1,13 @@
1
+ const express = require('express');
2
+
3
+ const app = express();
4
+
5
+ const port = process.env.BLOCKLET_PORT || 3030;
6
+
7
+ app.get('/', (req, res) => {
8
+ res.send('Hello World, Blocklet!');
9
+ });
10
+
11
+ app.listen(port, () => {
12
+ console.log(`Blocklet app listening on port ${port}`);
13
+ });
@@ -0,0 +1,22 @@
1
+ const path = require('path');
2
+ const AuthStorage = require('@arcblock/did-auth-storage-nedb');
3
+ const getWallet = require('@blocklet/sdk/lib/wallet');
4
+ const WalletAuthenticator = require('@blocklet/sdk/lib/wallet-authenticator');
5
+ const WalletHandler = require('@blocklet/sdk/lib/wallet-handler');
6
+
7
+ const env = require('./env');
8
+
9
+ const wallet = getWallet();
10
+ const authenticator = new WalletAuthenticator();
11
+ const handlers = new WalletHandler({
12
+ authenticator,
13
+ tokenStorage: new AuthStorage({
14
+ dbPath: path.join(env.dataDir, 'auth.db'),
15
+ }),
16
+ });
17
+
18
+ module.exports = {
19
+ authenticator,
20
+ handlers,
21
+ wallet,
22
+ };
@@ -0,0 +1,6 @@
1
+ const env = require('@blocklet/sdk/lib/env');
2
+
3
+ module.exports = {
4
+ ...env,
5
+ chainHost: process.env.CHAIN_HOST || '',
6
+ };
@@ -0,0 +1,3 @@
1
+ const logger = console;
2
+
3
+ module.exports = logger;
@@ -0,0 +1,3 @@
1
+ # template-react
2
+
3
+ A react template for creating a new blocklet project.
@@ -0,0 +1,57 @@
1
+ name: template-express
2
+ title: Blocklet Template Express
3
+ description: A Blocklet Api blocklet
4
+ keywords:
5
+ - blocklet
6
+ - react
7
+ group: dapp
8
+ did: ''
9
+ main: api/index.js
10
+ author:
11
+ name: Blocklet
12
+ email: blocklet@arcblock.io
13
+ repository:
14
+ type: git
15
+ url: 'git+https://github.com/blocklet/create-blocklet.git'
16
+ specVersion: 1.1.1
17
+ version: 0.1.0
18
+ logo: logo.png
19
+ files:
20
+ - logo.png
21
+ - README.md
22
+ - blocklet.md
23
+ - screenshots
24
+ - api/hooks/pre-start.js
25
+ interfaces:
26
+ - type: web
27
+ name: publicUrl
28
+ path: /
29
+ prefix: '*'
30
+ port: BLOCKLET_PORT
31
+ protocol: http
32
+ community: ''
33
+ documentation: ''
34
+ homepage: ''
35
+ license: ''
36
+ charging:
37
+ price: 0
38
+ tokens: []
39
+ shares: []
40
+ timeout:
41
+ start: 60
42
+ requirements:
43
+ server: '>=1.6.29'
44
+ os: '*'
45
+ cpu: '*'
46
+ scripts:
47
+ preStart: node api/hooks/pre-start.js
48
+ dev: npm run start
49
+ environments:
50
+ - name: CHAIN_HOST
51
+ description: What's endpoint of the chain?
52
+ required: true
53
+ default: 'https://beta.abtnetwork.io/api/'
54
+ secure: false
55
+ capabilities: {}
56
+ screenshots: []
57
+ children: []
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "express",
3
+ "version": "0.1.0",
4
+ "description": "",
5
+ "main": "api/index.js",
6
+ "scripts": {
7
+ "start": "NODE_ENV=development nodemon api/index.js -w api",
8
+ "clean": "rm -rf .blocklet",
9
+ "bundle": "npm run clean && blocklet bundle --zip --create-release",
10
+ "deploy": "npm run bundle && blocklet deploy .blocklet/bundle",
11
+ "upload": "npm run bundle && blocklet upload .blocklet/release/blocklet.json"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC",
16
+ "dependencies": {
17
+ "@arcblock/did-auth-storage-nedb": "^1.6.3",
18
+ "@blocklet/sdk": "^1.7.0",
19
+ "@ocap/client": "^1.15.2",
20
+ "dotenv-flow": "^3.2.0",
21
+ "express": "^4.17.3"
22
+ },
23
+ "devDependencies": {
24
+ "nodemon": "^2.0.15"
25
+ }
26
+ }
File without changes
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: 'next/core-web-vitals',
4
+ };
@@ -0,0 +1,153 @@
1
+ # Getting Started with Create Blocklet
2
+
3
+ This project was bootstrapped with [Create Blocklet](https://github.com/blocklet/create-blocklet).
4
+
5
+ This blocklet is a dapp project, which means this is a full-stack application. It's contained both `api` and `client` code.
6
+
7
+ ## File Structure
8
+
9
+ - public/ - static files
10
+ - favicon.ico - favicon
11
+ - screenshots/ - Screenshots
12
+ - api/ - Api side code
13
+ - hooks/ - blocklet lifecycle hooks
14
+ - libs/ - Api side libraries
15
+ - middlewares/ - Api side middlewares
16
+ - routes/ - Api side routes
17
+ - index.js - Api side entry point
18
+ - pages/ - next.js pages folder
19
+ - styles/ - next.js styles folder
20
+ - .env - Environment variables
21
+ - .env.local - Local environment variables
22
+ - .eslintrc.js - ESLint configuration
23
+ - .gitignore - Git ignore file
24
+ - .prettierrc - Prettier configuration
25
+ - blocklet.md - Blocklet README
26
+ - blocklet.yml - Blocklet configuration
27
+ - LICENSE - License file
28
+ - logo.png - Blocklet logo file
29
+ - Makefile - Makefile
30
+ - next.config.js - next.js config file
31
+ - package.json - Npm package file
32
+ - README.md - A guide for this blocklet
33
+ - version - Version file
34
+
35
+ ## Development
36
+
37
+ 1. Make sure you have [@blocklet/cli](https://www.npmjs.com/package/@blocklet/cli) installed
38
+
39
+ Blocklet needs blocklet server as a dependency. So you need to install it first.
40
+ `npm install -g @blocklet/cli`
41
+ See details in [https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#use-the-binary-distribution](https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#use-the-binary-distribution)
42
+
43
+ 2. Init blocklet server & start blocklet server
44
+
45
+ Before starting an blocklet server, you need to init blocklet server.
46
+ `blocklet server init --mode=debug`
47
+ `blocklet server start`
48
+ See details in [https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#configure-abt-node](https://docs.arcblock.io/abtnode/en/introduction/abtnode-setup#configure-abt-node)
49
+
50
+ 3. Go to the project directory `cd [name]`
51
+ 4. Install dependencies: `npm install` or `yarn`
52
+ 5. Start development server: `blocklet dev`
53
+
54
+ ## Bundle
55
+
56
+ After developing a blocklet, you may need to bundle it. Use `npm run bundle` command.
57
+
58
+ ## Deploy
59
+
60
+ - If you want to deploy this blocklet to local blocklet server, you can use `blocklet deploy .blocklet/bundle` command(Make sure the blocklet is bundled before deployment).
61
+ > Or you can simply use `npm run deploy` command.
62
+ - If you want to deploy this blocklet to remote blocklet server, you can use the command below.
63
+
64
+ ```shell
65
+ blocklet deploy .blocklet/bundle --endpoint {your blocklet server url} --access-key {blocklet server access key} --access-secret {blocklet server access secret}
66
+ ```
67
+
68
+ > Make sure the blocklet is bundled before deployment.
69
+
70
+ ## Upload to blocklet store
71
+
72
+ - If you want to upload the blocklet to any store for other users to download and use, you can following the following instructions.
73
+
74
+ Bump version at first.
75
+
76
+ ```shell
77
+ make bump-version
78
+ ```
79
+
80
+ Then config blocklet store url.
81
+ You can use those store url in below.
82
+
83
+ 1. [https://store.blocklet.dev/](https://store.blocklet.dev/)
84
+ 2. [https://dev.store.blocklet.dev/](https://dev.store.blocklet.dev/)
85
+ 3. A blocklet store started by yourself.
86
+ > Make sure you have installed a `blocklet store` on your own blocklet server. Check it on here: [https://store.blocklet.dev/blocklet/z8ia29UsENBg6tLZUKi2HABj38Cw1LmHZocbQ](https://store.blocklet.dev/blocklet/z8ia29UsENBg6tLZUKi2HABj38Cw1LmHZocbQ)
87
+
88
+ ```shell
89
+ blocklet config set store {store url}
90
+ ```
91
+
92
+ Get a `accessToken` by using this command.
93
+
94
+ > Why we need a `accessToken`?
95
+ > A `accessToken` is genrate by blocklet store, which help us upload our blocklet to any store.
96
+
97
+ Set `accessToken` to blocklet config
98
+
99
+ ```shell
100
+ blocklet config set accessToken {accessToken}
101
+ ```
102
+
103
+ Upload a new version to a store.
104
+
105
+ > Make sure the blocklet is bundled before upload.
106
+
107
+ ```shell
108
+ blocklet upload
109
+ ```
110
+
111
+ Or you can simply use `npm run upload` command.
112
+
113
+ - You also can upload a new version to a store by Github CI.
114
+ Bump version at first.
115
+
116
+ ```shell
117
+ make bump-version
118
+ ```
119
+
120
+ Push your code to Github main/master branch, or make a pull request to the main/master branch.
121
+ The CI workflow will automatically upload a new version to a store.
122
+
123
+ ## Q & A
124
+
125
+ 1. Q: How to change a blocklet's name?
126
+
127
+ A: Change the `name` field in the `package.json` file, change the `name` field in the `blocklet.yml` file.
128
+
129
+ You can also change the `title` field and `description` field in the `blocklet.yml` file.
130
+
131
+ Run `blocklet meta` command, you will get a `did` config, copy the `did` value.
132
+
133
+ Replace this command `"bundle:client": "PUBLIC_URL='/.blocklet/proxy/{did}' npm run build",` in `package.json`
134
+
135
+ Replace `did` field in the `blocklet.yml`
136
+
137
+ 2. Q: How to change a blocklet's logo?
138
+
139
+ Change the `logo.png` file root folder.
140
+
141
+ Or you can change the `logo` field in the `blocklet.yml` file.
142
+
143
+ > Make sure you have added the logo path to the `blocklet.yml` file `files` field.
144
+
145
+ ## Learn More
146
+
147
+ - Full specification of `blocklet.yml`: [https://github.com/blocklet/blocklet-specification/blob/main/docs/meta.md](https://github.com/blocklet/blocklet-specification/blob/main/docs/meta.md)
148
+ - Full document of Blocklet Server & blocklet development: [https://docs.arcblock.io/abtnode/en/introduction](https://docs.arcblock.io/abtnode/en/introduction)
149
+
150
+ ## License
151
+
152
+ The code is licensed under the Apache 2.0 license found in the
153
+ [LICENSE](LICENSE) file.
@@ -0,0 +1,33 @@
1
+ require('@blocklet/sdk/lib/error-handler');
2
+ require('dotenv-flow').config();
3
+
4
+ const Client = require('@ocap/client');
5
+
6
+ const env = require('../libs/env');
7
+ const logger = require('../libs/logger');
8
+ const { wallet } = require('../libs/auth');
9
+ const { name } = require('../../package.json');
10
+
11
+ const ensureAccountDeclared = async () => {
12
+ if (env.isComponent) return;
13
+ if (!env.chainHost) return;
14
+
15
+ const client = new Client(env.chainHost);
16
+ const { state } = await client.getAccountState({ address: wallet.toAddress() }, { ignoreFields: ['context'] });
17
+ if (!state) {
18
+ const hash = await client.declare({ moniker: name, wallet });
19
+ logger.log(`app account declared on chain ${env.chainHost}`, hash);
20
+ } else {
21
+ logger.log(`app account already declared on chain ${env.chainHost}`);
22
+ }
23
+ };
24
+
25
+ (async () => {
26
+ try {
27
+ await ensureAccountDeclared();
28
+ process.exit(0);
29
+ } catch (err) {
30
+ logger.error(`${name} pre-start error`, err.message);
31
+ process.exit(1);
32
+ }
33
+ })();
@@ -0,0 +1,24 @@
1
+ const express = require('express');
2
+ const next = require('next');
3
+
4
+ const port = parseInt(process.env.BLOCKLET_PORT, 10) || 3000;
5
+ const dev = process.env.NODE_ENV !== 'production';
6
+ const app = next({ dev });
7
+ const handle = app.getRequestHandler();
8
+
9
+ app.prepare().then(() => {
10
+ const server = express();
11
+
12
+ const router = express.Router();
13
+ router.use('/api', require('./routes'));
14
+ server.use(router);
15
+
16
+ server.all('*', (req, res) => {
17
+ return handle(req, res);
18
+ });
19
+
20
+ server.listen(port, (err) => {
21
+ if (err) throw err;
22
+ console.log(`> Ready on http://localhost:${port}`);
23
+ });
24
+ });
@@ -0,0 +1,22 @@
1
+ const path = require('path');
2
+ const AuthStorage = require('@arcblock/did-auth-storage-nedb');
3
+ const getWallet = require('@blocklet/sdk/lib/wallet');
4
+ const WalletAuthenticator = require('@blocklet/sdk/lib/wallet-authenticator');
5
+ const WalletHandler = require('@blocklet/sdk/lib/wallet-handler');
6
+
7
+ const env = require('./env');
8
+
9
+ const wallet = getWallet();
10
+ const authenticator = new WalletAuthenticator();
11
+ const handlers = new WalletHandler({
12
+ authenticator,
13
+ tokenStorage: new AuthStorage({
14
+ dbPath: path.join(env.dataDir, 'auth.db'),
15
+ }),
16
+ });
17
+
18
+ module.exports = {
19
+ authenticator,
20
+ handlers,
21
+ wallet,
22
+ };
@@ -0,0 +1,6 @@
1
+ const env = require('@blocklet/sdk/lib/env');
2
+
3
+ module.exports = {
4
+ ...env,
5
+ chainHost: process.env.CHAIN_HOST || '',
6
+ };
@@ -0,0 +1,3 @@
1
+ const logger = console;
2
+
3
+ module.exports = logger;
@@ -0,0 +1,6 @@
1
+ const middleware = require('@blocklet/sdk/lib/middlewares');
2
+ const router = require('express').Router();
3
+
4
+ router.use('/user', middleware.user(), (req, res) => res.json(req.user || {}));
5
+
6
+ module.exports = router;
@@ -0,0 +1,3 @@
1
+ # template-react
2
+
3
+ A react template for creating a new blocklet project.
@@ -0,0 +1,58 @@
1
+ name: template-nextjs
2
+ title: Blocklet Template Next.js
3
+ description: A Blocklet DAPP blocklet
4
+ keywords:
5
+ - blocklet
6
+ - react
7
+ group: dapp
8
+ did: ''
9
+ main: api/index.js
10
+ author:
11
+ name: Blocklet
12
+ email: blocklet@arcblock.io
13
+ repository:
14
+ type: git
15
+ url: 'git+https://github.com/blocklet/create-blocklet.git'
16
+ specVersion: 1.1.1
17
+ version: 0.1.0
18
+ logo: logo.png
19
+ files:
20
+ - .next
21
+ - logo.png
22
+ - README.md
23
+ - blocklet.md
24
+ - screenshots
25
+ - api/hooks/pre-start.js
26
+ interfaces:
27
+ - type: web
28
+ name: publicUrl
29
+ path: /
30
+ prefix: '*'
31
+ port: BLOCKLET_PORT
32
+ protocol: http
33
+ community: ''
34
+ documentation: ''
35
+ homepage: ''
36
+ license: ''
37
+ charging:
38
+ price: 0
39
+ tokens: []
40
+ shares: []
41
+ timeout:
42
+ start: 60
43
+ requirements:
44
+ server: '>=1.6.29'
45
+ os: '*'
46
+ cpu: '*'
47
+ scripts:
48
+ preStart: node api/hooks/pre-start.js
49
+ dev: npm run dev
50
+ environments:
51
+ - name: CHAIN_HOST
52
+ description: What's endpoint of the chain?
53
+ required: true
54
+ default: 'https://beta.abtnetwork.io/api/'
55
+ secure: false
56
+ capabilities: {}
57
+ screenshots: []
58
+ children: []
@@ -0,0 +1,6 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ };
5
+
6
+ module.exports = nextConfig;
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "nextjs",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "nodemon -w api api/index.js",
7
+ "build": "next build",
8
+ "start": "NODE_ENV=production node api/index.js",
9
+ "lint": "next lint",
10
+ "clean": "rm -rf .blocklet",
11
+ "bundle": "npm run bundle:client && npm run bundle:api",
12
+ "bundle:client": "PUBLIC_URL='/.blocklet/proxy/<%= did %>' npm run build",
13
+ "bundle:api": "npm run clean && blocklet bundle --zip --create-release",
14
+ "deploy": "npm run bundle && blocklet deploy .blocklet/bundle",
15
+ "upload": "npm run bundle && blocklet upload .blocklet/release/blocklet.json",
16
+ "prepare": "husky install"
17
+ },
18
+ "lint-staged": {
19
+ "*.{mjs,js,jsx,ts,tsx}": [
20
+ "prettier --write",
21
+ "eslint"
22
+ ],
23
+ "*.{css,less,scss,json,graphql}": [
24
+ "prettier --write"
25
+ ]
26
+ },
27
+ "dependencies": {
28
+ "@arcblock/did-auth": "^1.15.2",
29
+ "@arcblock/did-auth-storage-nedb": "^1.6.3",
30
+ "@blocklet/sdk": "^1.7.0",
31
+ "@ocap/client": "^1.15.2",
32
+ "@ocap/mcrypto": "^1.15.2",
33
+ "@ocap/wallet": "^1.15.2",
34
+ "dotenv-flow": "^3.2.0",
35
+ "express": "^4.17.3",
36
+ "next": "12.1.0",
37
+ "react": "17.0.2",
38
+ "react-dom": "17.0.2"
39
+ },
40
+ "devDependencies": {
41
+ "eslint": "8.11.0",
42
+ "eslint-config-next": "12.1.0",
43
+ "husky": "^7.0.4",
44
+ "lint-staged": "^12.3.7",
45
+ "nodemon": "^2.0.15",
46
+ "npm-run-all": "^4.1.5"
47
+ }
48
+ }
@@ -0,0 +1,21 @@
1
+ import Head from 'next/head';
2
+ import Script from 'next/script';
3
+ import '../styles/globals.css';
4
+
5
+ function MyApp({ Component, pageProps }) {
6
+ return (
7
+ <>
8
+ <Head>
9
+ <title>{process.env.APP_TITLE}</title>
10
+ <link rel="icon" href={`${process.env.PUBLIC_URL || ''}/favicon.ico`} />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
12
+ <meta name="theme-color" content="#4F6AF5" />
13
+ <meta name="description" content="Web site created using create-blocklet" />
14
+ </Head>
15
+ <Script src="__meta__.js" />
16
+ <Component {...pageProps} />
17
+ </>
18
+ );
19
+ }
20
+
21
+ export default MyApp;
@@ -0,0 +1,5 @@
1
+ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2
+
3
+ export default function handler(req, res) {
4
+ res.status(200).json({ name: 'John Doe' })
5
+ }
@@ -0,0 +1,69 @@
1
+ import Head from 'next/head'
2
+ import Image from 'next/image'
3
+ import styles from '../styles/Home.module.css'
4
+
5
+ export default function Home() {
6
+ return (
7
+ <div className={styles.container}>
8
+ <Head>
9
+ <title>Create Next App</title>
10
+ <meta name="description" content="Generated by create next app" />
11
+ <link rel="icon" href="/favicon.ico" />
12
+ </Head>
13
+
14
+ <main className={styles.main}>
15
+ <h1 className={styles.title}>
16
+ Welcome to <a href="https://nextjs.org">Next.js!</a>
17
+ </h1>
18
+
19
+ <p className={styles.description}>
20
+ Get started by editing{' '}
21
+ <code className={styles.code}>pages/index.js</code>
22
+ </p>
23
+
24
+ <div className={styles.grid}>
25
+ <a href="https://nextjs.org/docs" className={styles.card}>
26
+ <h2>Documentation &rarr;</h2>
27
+ <p>Find in-depth information about Next.js features and API.</p>
28
+ </a>
29
+
30
+ <a href="https://nextjs.org/learn" className={styles.card}>
31
+ <h2>Learn &rarr;</h2>
32
+ <p>Learn about Next.js in an interactive course with quizzes!</p>
33
+ </a>
34
+
35
+ <a
36
+ href="https://github.com/vercel/next.js/tree/canary/examples"
37
+ className={styles.card}
38
+ >
39
+ <h2>Examples &rarr;</h2>
40
+ <p>Discover and deploy boilerplate example Next.js projects.</p>
41
+ </a>
42
+
43
+ <a
44
+ href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
45
+ className={styles.card}
46
+ >
47
+ <h2>Deploy &rarr;</h2>
48
+ <p>
49
+ Instantly deploy your Next.js site to a public URL with Vercel.
50
+ </p>
51
+ </a>
52
+ </div>
53
+ </main>
54
+
55
+ <footer className={styles.footer}>
56
+ <a
57
+ href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
58
+ target="_blank"
59
+ rel="noopener noreferrer"
60
+ >
61
+ Powered by{' '}
62
+ <span className={styles.logo}>
63
+ <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
64
+ </span>
65
+ </a>
66
+ </footer>
67
+ </div>
68
+ )
69
+ }
@@ -0,0 +1,116 @@
1
+ .container {
2
+ padding: 0 2rem;
3
+ }
4
+
5
+ .main {
6
+ min-height: 100vh;
7
+ padding: 4rem 0;
8
+ flex: 1;
9
+ display: flex;
10
+ flex-direction: column;
11
+ justify-content: center;
12
+ align-items: center;
13
+ }
14
+
15
+ .footer {
16
+ display: flex;
17
+ flex: 1;
18
+ padding: 2rem 0;
19
+ border-top: 1px solid #eaeaea;
20
+ justify-content: center;
21
+ align-items: center;
22
+ }
23
+
24
+ .footer a {
25
+ display: flex;
26
+ justify-content: center;
27
+ align-items: center;
28
+ flex-grow: 1;
29
+ }
30
+
31
+ .title a {
32
+ color: #0070f3;
33
+ text-decoration: none;
34
+ }
35
+
36
+ .title a:hover,
37
+ .title a:focus,
38
+ .title a:active {
39
+ text-decoration: underline;
40
+ }
41
+
42
+ .title {
43
+ margin: 0;
44
+ line-height: 1.15;
45
+ font-size: 4rem;
46
+ }
47
+
48
+ .title,
49
+ .description {
50
+ text-align: center;
51
+ }
52
+
53
+ .description {
54
+ margin: 4rem 0;
55
+ line-height: 1.5;
56
+ font-size: 1.5rem;
57
+ }
58
+
59
+ .code {
60
+ background: #fafafa;
61
+ border-radius: 5px;
62
+ padding: 0.75rem;
63
+ font-size: 1.1rem;
64
+ font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
65
+ Bitstream Vera Sans Mono, Courier New, monospace;
66
+ }
67
+
68
+ .grid {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ flex-wrap: wrap;
73
+ max-width: 800px;
74
+ }
75
+
76
+ .card {
77
+ margin: 1rem;
78
+ padding: 1.5rem;
79
+ text-align: left;
80
+ color: inherit;
81
+ text-decoration: none;
82
+ border: 1px solid #eaeaea;
83
+ border-radius: 10px;
84
+ transition: color 0.15s ease, border-color 0.15s ease;
85
+ max-width: 300px;
86
+ }
87
+
88
+ .card:hover,
89
+ .card:focus,
90
+ .card:active {
91
+ color: #0070f3;
92
+ border-color: #0070f3;
93
+ }
94
+
95
+ .card h2 {
96
+ margin: 0 0 1rem 0;
97
+ font-size: 1.5rem;
98
+ }
99
+
100
+ .card p {
101
+ margin: 0;
102
+ font-size: 1.25rem;
103
+ line-height: 1.5;
104
+ }
105
+
106
+ .logo {
107
+ height: 1em;
108
+ margin-left: 0.5rem;
109
+ }
110
+
111
+ @media (max-width: 600px) {
112
+ .grid {
113
+ width: 100%;
114
+ flex-direction: column;
115
+ }
116
+ }
@@ -0,0 +1,16 @@
1
+ html,
2
+ body {
3
+ padding: 0;
4
+ margin: 0;
5
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6
+ Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7
+ }
8
+
9
+ a {
10
+ color: inherit;
11
+ text-decoration: none;
12
+ }
13
+
14
+ * {
15
+ box-sizing: border-box;
16
+ }
@@ -1,6 +1,6 @@
1
1
  name: template-react
2
2
  title: Blocklet Template React
3
- description: A Blocklet Server blocklet
3
+ description: A Blocklet DAPP blocklet
4
4
  keywords:
5
5
  - blocklet
6
6
  - react
@@ -1,6 +1,6 @@
1
1
  name: template-vue
2
2
  title: Blocklet Template Vue
3
- description: A Blocklet Server blocklet
3
+ description: A Blocklet DAPP blocklet
4
4
  keywords:
5
5
  - blocklet
6
6
  - vue
@@ -46,7 +46,8 @@
46
46
  "nodemon": "^2.0.15",
47
47
  "npm-run-all": "^4.1.5",
48
48
  "prettier": "^2.6.0",
49
- "vite": "^2.8.6"
49
+ "vite": "^2.8.6",
50
+ "vite-plugin-blocklet": "^0.1.1"
50
51
  },
51
52
  "lint-staged": {
52
53
  "*.{mjs,js,vue}": [
@@ -1,6 +1,7 @@
1
1
  import { defineConfig, loadEnv } from 'vite';
2
2
  import vue from '@vitejs/plugin-vue';
3
3
  import { createHtmlPlugin } from 'vite-plugin-html';
4
+ import { createBlockletPlugin } from 'vite-plugin-blocklet';
4
5
 
5
6
  // https://vitejs.dev/config/
6
7
  export default ({ mode }) => {
@@ -17,6 +18,7 @@ export default ({ mode }) => {
17
18
  },
18
19
  },
19
20
  }),
21
+ createBlockletPlugin(),
20
22
  ],
21
23
  server: {
22
24
  port: process.env.BLOCKLET_PORT,
@@ -1,6 +1,6 @@
1
1
  name: template-vue2
2
2
  title: Blocklet Template Vue2
3
- description: A Blocklet Server blocklet
3
+ description: A Blocklet DAPP blocklet
4
4
  keywords:
5
5
  - blocklet
6
6
  - vue
@@ -26,7 +26,8 @@
26
26
  "husky": "^7.0.4",
27
27
  "lint-staged": "^12.3.7",
28
28
  "prettier": "^2.6.0",
29
- "vite": "^2.8.6"
29
+ "vite": "^2.8.6",
30
+ "vite-plugin-blocklet": "^0.1.1"
30
31
  },
31
32
  "lint-staged": {
32
33
  "*.{mjs,js,vue}": [
@@ -1,6 +1,7 @@
1
1
  import { defineConfig, loadEnv } from 'vite';
2
2
  import vue from '@vitejs/plugin-vue';
3
3
  import { createHtmlPlugin } from 'vite-plugin-html';
4
+ import { createBlockletPlugin } from 'vite-plugin-blocklet';
4
5
 
5
6
  // https://vitejs.dev/config/
6
7
  export default ({ mode }) => {
@@ -17,6 +18,7 @@ export default ({ mode }) => {
17
18
  },
18
19
  },
19
20
  }),
21
+ createBlockletPlugin(),
20
22
  ],
21
23
  server: {
22
24
  port: process.env.BLOCKLET_PORT,