diff --git a/README.md b/README.md
index 536d6062ac476f35aa47bb2f850c9346895bd755..d62c8ffeefab515e11ed780dbaf4f8c60a91369e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-### 介绍
+### 介绍
`uni-starter`,是一个云端一体的、集成了商用项目开发常见功能的项目模板。
如果说uniCloud admin是管理端项目的基础模板,那么uni-starter则是用户端、尤其是移动端的基础模板。
在这个模板基础之上快速填充自己的业务,即可很快完成一个应用。
diff --git a/changelog.md b/changelog.md
index d49fd6ea44b89c5137ac8ee2d4ccc3f025f573b2..6da33be6066ce57a23f97bea23bfbb3a07653fdb 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,7 @@
+## 1.0.5(2021-05-20)
+改用uni_modules方式处理图片选择api时无权限,引导用户快捷打开系统设置
## 1.0.4(2021-05-19)
-12
+为方便部署,添加空的manifest.json uni-config-center下的uni-id配置
## 1.0.3(2021-05-18)
重大调整,原云函数名称:user-center改名叫uni-id-cf
修复,绑定手机号码场景。因手机未插SIM导致的一键登录失败后未直接跳到获取短信验证码方式绑定
diff --git a/common/appInit.js b/common/appInit.js
index 8dc20e2a6b2b5b774b3054149889a7ccf1db6a5e..2e746ef1ae766c881b0df6e19b087671a544d236 100644
--- a/common/appInit.js
+++ b/common/appInit.js
@@ -3,6 +3,7 @@ import uniStarterConfig from '@/uni-starter.config.js';
// #ifdef APP-PLUS
import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version';
+import interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js';
// #endif
export default function() {
@@ -14,9 +15,13 @@ export default function() {
// 初始化appVersion(仅app生效)
initAppVersion();
- /*
- 这里应用了[拦截器](https://uniapp.dcloud.io/api/interceptor?id=addinterceptor)实现了,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面
- */
+
+ // #ifdef APP-PLUS
+ // 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面
+ interceptorChooseImage()
+ // #endif
+
+
//自定义路由拦截
const {"router": {needLogin,login} } = uniStarterConfig //需要登录的页面
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
@@ -54,39 +59,6 @@ export default function() {
},
})
})
-
- //当应用无访问摄像头/相册权限,引导跳到设置界面
- uni.addInterceptor('chooseImage', {
- fail(e) { // 失败回调拦截
- console.log(e);
- if(uni.getSystemInfoSync().platform == "android" && e.errMsg == 'chooseImage:fail No Permission'){
- if(e.code === 11){
- uni.showModal({
- title:"无法访问摄像头",
- content: "当前无摄像头访问权限,建议前往设置",
- confirmText: "前往设置",
- success(e) {
- if (e.confirm) {
- openAppPermissionSetting()
- }
- }
- });
- }else{
- uni.showModal({
- title:"无法访问相册",
- content: "当前无系统相册访问权限,建议前往设置",
- confirmText: "前往设置",
- success(e) {
- if (e.confirm) {
- openAppPermissionSetting()
- }
- }
- });
- }
- }
- }
- })
-
// #ifdef APP-PLUS
// 监听并提示设备网络状态变化
uni.onNetworkStatusChange(res=> {
@@ -139,30 +111,4 @@ function initAppVersion() {
// 检查更新
checkUpdate();
// #endif
-}
-
-function openAppPermissionSetting(){
- // 跳转到**应用**的权限页面
- if (uni.getSystemInfoSync().platform == "ios") {
- var UIApplication = plus.ios.import("UIApplication");
- var application2 = UIApplication.sharedApplication();
- var NSURL2 = plus.ios.import("NSURL");
- // var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
- var setting2 = NSURL2.URLWithString("app-settings:");
- application2.openURL(setting2);
- plus.ios.deleteObject(setting2);
- plus.ios.deleteObject(NSURL2);
- plus.ios.deleteObject(application2);
- } else {
- // console.log(plus.device.vendor);
- var Intent = plus.android.importClass("android.content.Intent");
- var Settings = plus.android.importClass("android.provider.Settings");
- var Uri = plus.android.importClass("android.net.Uri");
- var mainActivity = plus.android.runtimeMainActivity();
- var intent = new Intent();
- intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
- intent.setData(uri);
- mainActivity.startActivity(intent);
- }
}
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index 277967fdebcbbdc81ffde861dbf2727fafc8e7b9..35e724883f8bea739416cbc441dee5399156bc93 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1 +1,100 @@
-{"name":"uni-starter","appid":"","description":"集成了商用项目开发常见功能的项目模板","versionName":"1.0.0","versionCode":"100","transformPx":false,"app-plus":{"privacy":{"prompt":"template","template":{"title":"服务协议和隐私政策","message":" 请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。
你可阅读《服务协议》和《隐私政策》了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。","buttonAccept":"同意","buttonRefuse":"暂不同意"}},"compatible":{"ignoreVersion":true},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"splashscreen":{"alwaysShowBeforeRender":true,"waiting":true,"autoclose":true,"delay":0},"modules":{"Fingerprint":{},"Share":{},"Push":{},"OAuth":{}},"distribute":{"android":{"permissions":["","","","","","","","","","","","","","","","",""],"abiFilters":["armeabi-v7a","arm64-v8a","x86"]},"ios":{},"sdkConfigs":{"oauth":{"univerify":{},"apple":{}},"ad":{},"share":{},"geolocation":{},"push":{"unipush":{}},"payment":{}}},"nvueLaunchMode":""},"quickapp":{},"mp-weixin":{"appid":"","setting":{"urlCheck":false,"es6":false},"usingComponents":true,"betterScopedSlots":true},"mp-alipay":{"usingComponents":true},"mp-baidu":{"usingComponents":true},"mp-toutiao":{"usingComponents":true},"uniStatistics":{"enable":false},"h5":{"template":""}}
\ No newline at end of file
+{
+ "name": "uni-starter",
+ "appid": "",
+ "description": "云端一体应用快速开发模版",
+ "versionName": "1.0.0",
+ "versionCode": "100",
+ "transformPx": false,
+ "app-plus": {
+ "privacy": {
+ "prompt": "template",
+ "template": {
+ "title": "服务协议和隐私政策",
+ "message": " 请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。
你可阅读《服务协议》和《隐私政策》了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
+ "buttonAccept": "同意",
+ "buttonRefuse": "暂不同意"
+ }
+ },
+ "compatible": {
+ "ignoreVersion": true
+ },
+ "usingComponents": true,
+ "nvueStyleCompiler": "uni-app",
+ "compilerVersion": 3,
+ "splashscreen": {
+ "alwaysShowBeforeRender": true,
+ "waiting": true,
+ "autoclose": true,
+ "delay": 0
+ },
+ "modules": {
+ "Fingerprint": {},
+ "Share": {},
+ "Push": {},
+ "OAuth": {}
+ },
+ "distribute": {
+ "android": {
+ "permissions": ["",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ],
+ "abiFilters": ["armeabi-v7a", "arm64-v8a", "x86"]
+ },
+ "ios": {},
+ "sdkConfigs": {
+ "oauth": {
+ "univerify": {},
+ "apple": {}
+ },
+ "ad": {},
+ "share": {},
+ "geolocation": {},
+ "push": {
+ "unipush": {}
+ },
+ "payment": {}
+ }
+ },
+ "nvueLaunchMode": ""
+ },
+ "quickapp": {},
+ "mp-weixin": {
+ "appid": "",
+ "setting": {
+ "urlCheck": false,
+ "es6": false
+ },
+ "usingComponents": true,
+ "betterScopedSlots": true
+ },
+ "mp-alipay": {
+ "usingComponents": true
+ },
+ "mp-baidu": {
+ "usingComponents": true
+ },
+ "mp-toutiao": {
+ "usingComponents": true
+ },
+ "uniStatistics": {
+ "enable": false
+ },
+ "h5": {
+ "template": ""
+ }
+}
diff --git a/uniCloud-aliyun/cloudfunctions/uni-id-cf/index.js b/uniCloud-aliyun/cloudfunctions/uni-id-cf/index.js
index 9e959310acc5074b7ac97cef769bbebe9f5f540d..37c947f6640fbb8e531566f9e1a7d1e6735186bb 100644
--- a/uniCloud-aliyun/cloudfunctions/uni-id-cf/index.js
+++ b/uniCloud-aliyun/cloudfunctions/uni-id-cf/index.js
@@ -200,15 +200,12 @@ exports.main = async (event, context) => {
res = await uniID.logout(event.uniIdToken)
break;
case 'sendSmsCode':
-
// 测试期间短信统一用 123456 正式项目删除即可
return uniID.setVerifyCode({
mobile: params.mobile,
code:'123456',
type: params.type
})
-
-
// 简单限制一下客户端调用频率
const ipLimit = await db.collection('uni-verify').where({
ip: context.CLIENTIP,
@@ -313,6 +310,21 @@ exports.main = async (event, context) => {
break;
case 'refreshCaptcha':
res = await uniCaptcha.refresh(params)
+ break;
+ case 'registerAdmin':
+ let {username,password} = params
+ let {total} = await db.collection('uni-id-users').where({role: 'admin'}).count()
+ if (total) {
+ return {
+ code: 10001,
+ message: '超级管理员已存在,请登录...'
+ }
+ }
+ return this.ctx.uniID.register({
+ username,
+ password,
+ role: ["admin"]
+ })
break;
default:
res = {
@@ -321,7 +333,6 @@ exports.main = async (event, context) => {
}
break;
}
-
//返回数据给客户端
return res
};
diff --git a/uni_modules.config.json b/uni_modules.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..395fea795fe162a14391232e9f3ae5763dd61702
--- /dev/null
+++ b/uni_modules.config.json
@@ -0,0 +1,6 @@
+{
+ "scripts": {
+ "preupload": "node /Users/dcloud_linju/Desktop/uni_modules_tool/tool.js change",
+ "postupload": "node /Users/dcloud_linju/Desktop/uni_modules_tool/tool.js recovery"
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/json-interceptor-chooseImage/changelog.md b/uni_modules/json-interceptor-chooseImage/changelog.md
new file mode 100644
index 0000000000000000000000000000000000000000..192e843f8af5fdb90b9969813141c1205561fb4d
--- /dev/null
+++ b/uni_modules/json-interceptor-chooseImage/changelog.md
@@ -0,0 +1,4 @@
+## 1.0.1(2021-05-20)
+新增文档和示例代码
+## 1.0.0(2021-05-20)
+第一版本发布
diff --git a/uni_modules/json-interceptor-chooseImage/js_sdk/main.js b/uni_modules/json-interceptor-chooseImage/js_sdk/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..710d7ba48c845f111638e672ad8cd1c095d1ed7a
--- /dev/null
+++ b/uni_modules/json-interceptor-chooseImage/js_sdk/main.js
@@ -0,0 +1,70 @@
+export default function(){
+ //当应用无访问摄像头/相册权限,引导跳到设置界面
+ uni.addInterceptor('chooseImage', {
+ fail(e) { // 失败回调拦截 更多拦截器用法 [详情](https://uniapp.dcloud.io/api/interceptor?id=addinterceptor)
+ console.log(e);
+ if (uni.getSystemInfoSync().platform == "android" && e.errMsg == 'chooseImage:fail No Permission') {
+ if (e.code === 11) {
+ uni.showModal({
+ title: "无法访问摄像头",
+ content: "当前无摄像头访问权限,建议前往设置",
+ confirmText: "前往设置",
+ success(e) {
+ if (e.confirm) {
+ gotoAppPermissionSetting()
+ }
+ }
+ });
+ } else {
+ uni.showModal({
+ title: "无法访问相册",
+ content: "当前无系统相册访问权限,建议前往设置",
+ confirmText: "前往设置",
+ success(e) {
+ if (e.confirm) {
+ gotoAppPermissionSetting()
+ }
+ }
+ });
+ }
+ } else if (e.errCode === 2&&e.errMsg == "chooseImage:fail No filming permission") {
+ console.log('e.errMsg === 2 ios无法拍照权限 ');
+ // 注:e.errCode === 8 ios无从相册选择图片的权限 api已内置无需自己用拦截器实现
+ uni.showModal({
+ title: "无法访问相册",
+ content: "当前无系统相册访问权限,建议前往设置",
+ confirmText: "前往设置",
+ success(e) {
+ if (e.confirm) {
+ gotoAppPermissionSetting()
+ }
+ }
+ });
+ }
+ }
+ })
+
+ //跳转到**应用**的权限页面 参考来源:https://ext.dcloud.net.cn/plugin?id=594
+ function gotoAppPermissionSetting() {
+ if (uni.getSystemInfoSync().platform == "ios") {
+ var UIApplication = plus.ios.import("UIApplication");
+ var application2 = UIApplication.sharedApplication();
+ var NSURL2 = plus.ios.import("NSURL");
+ var setting2 = NSURL2.URLWithString("app-settings:");
+ application2.openURL(setting2);
+ plus.ios.deleteObject(setting2);
+ plus.ios.deleteObject(NSURL2);
+ plus.ios.deleteObject(application2);
+ } else {
+ var Intent = plus.android.importClass("android.content.Intent");
+ var Settings = plus.android.importClass("android.provider.Settings");
+ var Uri = plus.android.importClass("android.net.Uri");
+ var mainActivity = plus.android.runtimeMainActivity();
+ var intent = new Intent();
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
+ intent.setData(uri);
+ mainActivity.startActivity(intent);
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/json-interceptor-chooseImage/package.json b/uni_modules/json-interceptor-chooseImage/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..d349d5a3e17413d047f11f4c141ec23706d76f7a
--- /dev/null
+++ b/uni_modules/json-interceptor-chooseImage/package.json
@@ -0,0 +1,76 @@
+{
+ "id": "json-interceptor-chooseImage",
+ "displayName": "拦截器应用示例:图片选择api时无权限,引导用户快捷打开系统设置",
+ "version": "1.0.1",
+ "description": "拦截器应用示例:图片选择api时无权限,引导用户快捷打开系统设置",
+ "keywords": [
+ "interceptor,拦截器,相册权限"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "category": [
+ "JS SDK",
+ "通用 SDK"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "n",
+ "Android Browser": "n",
+ "微信浏览器(Android)": "n",
+ "QQ浏览器(Android)": "n"
+ },
+ "H5-pc": {
+ "Chrome": "n",
+ "IE": "n",
+ "Edge": "n",
+ "Firefox": "n",
+ "Safari": "n"
+ },
+ "小程序": {
+ "微信": "n",
+ "阿里": "n",
+ "百度": "n",
+ "字节跳动": "n",
+ "QQ": "n"
+ },
+ "快应用": {
+ "华为": "n",
+ "联盟": "n"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/json-interceptor-chooseImage/readme.md b/uni_modules/json-interceptor-chooseImage/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..dc74da4fe00b9815a6059b70eb65ba1f7b45e7c4
--- /dev/null
+++ b/uni_modules/json-interceptor-chooseImage/readme.md
@@ -0,0 +1,30 @@
+拦截器顾名思义,是在框架方法执行的各个环节(包含:拦截前触发、成功回调拦截、失败回调拦截、完成回调拦截)
+插入逻辑,篡改参数或终止运行。
+#### 优势
+- 这种方式相当于改写了api的内部逻辑,相比语法糖他更加直观,不影响ide的代码提示。
+- 将常规固定的逻辑封装到框架的api内,且支持个性化设计。如你可以在本插件路径:`/uni_modules/json-interceptor-chooseImage/js_sdk/main.js`修改弹出框元素,绘制更漂亮的样式和文字说明。
+
+#### 使用示例,App.vue页代码如下:
+```
+
+```
+
+> 跳转到**应用**的权限页面 参考来源:[https://ext.dcloud.net.cn/plugin?id=594](https://ext.dcloud.net.cn/plugin?id=594) 感谢作者@DCloud_heavensoft
diff --git a/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json b/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
index fce981e3cf95713b3b51deb2e533b6c741a4f29d..ea9fae7b7fc5f5bb973f6f9273fdf0aba5433b14 100644
--- a/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
+++ b/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
@@ -1,52 +1,65 @@
+/*
+ 重要的事情说三遍:
+ 本文件不能有任何注释,删除后才能正常使用
+ 本文件不能有任何注释,删除后才能正常使用
+ 本文件不能有任何注释,删除后才能正常使用
+*/
{
- "passwordSecret": "passwordSecret-demo",
- "tokenSecret": "tokenSecret-demo",
- "tokenExpiresIn": 7200,
- "tokenExpiresThreshold": 600,
- "passwordErrorLimit": 6,
- "bindTokenToDevice": true,
- "passwordErrorRetryTime": 3600,
- "autoSetInviteCode": false,
- "forceInviteCode": false,
- "app-plus": {
- "tokenExpiresIn": 2592000,
- "oauth": {
- "weixin": {
- "appid": "",
- "appsecret": ""
- },
- "apple": {
- "bundleId": ""
- }
- }
- },
- "mp-weixin": {
- "oauth": {
- "weixin": {
- "appid": "",
- "appsecret": ""
- }
- }
- },
- "mp-alipay": {
- "oauth": {
- "alipay": {
- "appid": "",
- "privateKey": ""
- }
- }
- },
- "service": {
- "sms": {
- "name": "DCloud",
- "codeExpiresIn": 300,
- "smsKey": "",
- "smsSecret": ""
- },
- "univerify": {
- "appid": "",
- "apiKey": "",
- "apiSecret": ""
- }
- }
+ "passwordSecret": "passwordSecret-demo", // 数据库中password字段是加密存储的,这里的passwordSecret即为加密密码所用的密钥,注意修改为自己的密钥,使用一个较长的字符串即可
+ "tokenSecret": "tokenSecret-demo", // 生成token所用的密钥,注意修改为自己的,使用一个较长的字符串即可
+ "tokenExpiresIn": 7200, // 全平台token过期时间,未指定过期时间的平台会使用此值
+ "tokenExpiresThreshold": 600, // 新增于uni-id 1.1.7版本,checkToken时如果token有效期小于此值则自动获取新token,请注意将新token返回给前端保存,如果不配置此参数则不开启自动获取新token功能
+ "bindTokenToDevice": false, // 是否将token和设备绑定,设置为true会进行ua校验,uni-id 3.0.12前默认为true,3.0.12及以后版本默认调整为false
+ "passwordErrorLimit": 6, // 密码错误最大重试次数
+ "passwordErrorRetryTime": 3600, // 密码错误重试次数超限之后的冻结时间
+ "autoSetInviteCode": false, // 是否在用户注册时自动设置邀请码,默认不自动设置
+ "forceInviteCode": false, // 是否强制用户注册时必填邀请码,默认为false(需要注意的是目前只有短信验证码注册才可以填写邀请码),设置为true时需要在loginBySms时指定type为register来使用注册,登录时也要传入type为login
+ "removePermissionAndRoleFromToken": false, // 新增于uni-id 3.0.0版本,如果配置为false则自动缓存用户的角色、权限到token中,默认值为false。详细说明见https://uniapp.dcloud.io/uniCloud/uni-id?id=cachepermissionintoken
+ "app-plus": {
+ "tokenExpiresIn": 2592000,
+ "oauth": {
+ // App微信登录所用到的appid、appsecret需要在微信开放平台获取,注意:不是公众平台而是开放平台
+ "weixin": {
+ "appid": "weixin appid",
+ "appsecret": "weixin appsecret"
+ },
+ "apple": { // 使用苹果登录时需要
+ "bundleId": "your bundleId"
+ }
+ }
+ },
+ "mp-weixin": {
+ "tokenExpiresIn": 259200,
+ "oauth": {
+ // 微信小程序登录所用的appid、appsecret需要在对应的小程序管理控制台获取
+ "weixin": {
+ "appid": "weixin appid",
+ "appsecret": "weixin appsecret"
+ }
+ }
+ },
+ "mp-alipay": {
+ "tokenExpiresIn": 259200,
+ "oauth": {
+ // 支付宝小程序登录用到的appid、privateKey请参考支付宝小程序的文档进行设置或者获取,https://opendocs.alipay.com/open/291/105971#LDsXr
+ "alipay": {
+ "appid": "alipay appid",
+ "privateKey": "alipay privateKey", // 私钥
+ "keyType": "PKCS8" // 私钥类型,如果私钥类型不是PKCS8,需要填写此字段,否则会出现“error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag”错误
+ }
+ }
+ },
+ "service": {
+ "sms": {
+ "name": "your app name", // 应用名称,对应短信模版的name
+ "codeExpiresIn": 180, // 验证码过期时间,单位为秒,注意一定要是60的整数倍
+ "smsKey": "your sms key", // 短信密钥key,开通短信服务处可以看到
+ "smsSecret": "your sms secret" // 短信密钥secret,开通短信服务处可以看到
+ },
+ "univerify": {
+ "appid": "your appid", // 当前应用的appid,使用云函数URL化,此项必须配置
+ "apiKey": "your apiKey",// apiKey 和 apiSecret 在开发者中心获取,开发者中心:https://dev.dcloud.net.cn/uniLogin/index?type=0,文档:https://ask.dcloud.net.cn/article/37965
+ "apiSecret": "your apiSecret"
+ }
+ }
}
\ No newline at end of file