Compare commits

...

3 Commits

Author SHA1 Message Date
ed92f62a40 完善文档信息 2025-08-04 16:51:11 +08:00
ff98b783df 区分环境 2025-08-04 16:34:29 +08:00
774bf79897 fix 配置文件 2025-08-04 16:06:01 +08:00
10 changed files with 133 additions and 81 deletions

32
apple_cert.md Normal file
View File

@ -0,0 +1,32 @@
## 苹果推送服务证书申请流程
### 1. 申请CRS文件
1. 在macos下使用 钥匙串服务
2. ![img.png](img.png)
3. 填写信息
3.1 用户电子邮箱地址: 填写项目的苹果开发者账号的邮箱,很重要!!
3.2 常用名称填写和app相关的名称: dimension push apns
3.3 CA电子邮箱地址 留空
3.4 请求是:一定要选择存储到磁盘
4. 点击“继续”会生成CSR文件这个文件用用到在第二步里面用来在苹果后台生成 cer文件
### 2. 登陆苹果开发后台
2.1: Certificates => 选择添加 => Services => Apple Push Notification service SSL (Sandbox & Production)
2.2 选择第一步生成的 CSR 文件,下载 cer文件
2.3 选择用 "钥匙串服务" 打开
2.4 在登陆项下,选择密钥,选择对应的文件, 并导出为 .p12 文件格式
![img_1.png](img_1.png)
2.5 将p12文件转换成推送服务器需要pem文件
openssl pkcs12 -in apns_push_key.p12 -out apns_push_key.pem -nodes -legacy
添加-legacy参数可以让 OpenSSL 使用传统加密算法支持模式,这通常能解决兼容性问题。 之前没有使用 -legacy参数导致解析失败!!
2.6 将pem文件分离成.cert和.key文件
# 提取证书部分到cert.pem
openssl x509 -in combined.pem -out cert.pem
# 提取私钥部分到private.key
openssl rsa -in combined.pem -out private.key
### 3. 推送服务的证书有效为1年需要及时更新; 统一了开发环境和正式环境的证书
3.1 开发环境 api.sandbox.push.apple.com
3.2 正式环境 api.push.apple.com
### 4. 研究整个流程花费了1天时间

View File

@ -1,38 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIGkzCCBXugAwIBAgIQYacVGyUDb+FSqLp9ewYcmDANBgkqhkiG9w0BAQsFADB1
MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzQxEzARBgNVBAoMCkFw
cGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTI1MDQwMjA4NTk0N1oXDTI2MDUwMjA4
NTk0NlowgYYxJTAjBgoJkiaJk/IsZAEBDBVjb20uamloZS5kaW1lbnNpb25odWIx
OzA5BgNVBAMMMkFwcGxlIFNhbmRib3ggUHVzaCBTZXJ2aWNlczogY29tLmppaGUu
ZGltZW5zaW9uaHViMRMwEQYDVQQLDApQRjNRRzgzN1hTMQswCQYDVQQGEwJDTjCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALIb4K9MBx+14E0DMOgNpqW1
Dlid/d0w3DNU3H0sJ5MykqEMjXHC3itQfvA+w5bg4KLYR9UdHz596OzsHiuxKJeZ
NjC6xOp7mC63d7qHUI+xHs8dI5URadqldLtRtV+zZNF3DZn2lAbm+UL+Ybu+MSKl
HrZLpSREmUmqRIKpXNcV9IwKMJPFfEAo5aMUzFUO4aAuoGgrT2F/0myb7lyUvxbK
R8W3/BE15f2saCLf4aSiWzZQMtwoiDTEKVBksd+MPDw46L4RDa/hUdHp9GlIh0iT
/MgZSCn1Tk81BJ34fGrHjejtGBEwJZWH8qu1T0OjFRhbhxZ980JizNiKg3LU9mEC
AwEAAaOCAwswggMHMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUW9n6HeeaGguj
mXYiUIY+kchbd6gwcAYIKwYBBQUHAQEEZDBiMC0GCCsGAQUFBzAChiFodHRwOi8v
Y2VydHMuYXBwbGUuY29tL3d3ZHJnNC5kZXIwMQYIKwYBBQUHMAGGJWh0dHA6Ly9v
Y3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcmc0MDMwggEeBgNVHSAEggEVMIIBETCC
AQ0GCSqGSIb3Y2QFATCB/zCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0
aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ug
b2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0
aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlv
biBwcmFjdGljZSBzdGF0ZW1lbnRzLjA3BggrBgEFBQcCARYraHR0cHM6Ly93d3cu
YXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5LzATBgNVHSUEDDAKBggrBgEF
BQcDAjAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmFwcGxlLmNvbS93d2Ry
ZzQtNS5jcmwwHQYDVR0OBBYEFNpwVOugarBGNv49JwM/dMvG0M4ZMA4GA1UdDwEB
/wQEAwIHgDCBtwYKKoZIhvdjZAYDBgSBqDCBpQwVY29tLmppaGUuZGltZW5zaW9u
aHViMAcMBXRvcGljDBpjb20uamloZS5kaW1lbnNpb25odWIudm9pcDAGDAR2b2lw
DCJjb20uamloZS5kaW1lbnNpb25odWIuY29tcGxpY2F0aW9uMA4MDGNvbXBsaWNh
dGlvbgweY29tLmppaGUuZGltZW5zaW9uaHViLnZvaXAtcHR0MAsMCS52b2lwLXB0
dDAQBgoqhkiG92NkBgMBBAIFADANBgkqhkiG9w0BAQsFAAOCAQEAgpYnPL7ih9Ho
BQitqWa0N8ta++XjmHaT+eumvuk7TujaW8p0mczX+B4aFghhygSuBuFXOcWcrsdd
OcaH+rUzOt60bdtUeh7m9eMqwAuYUUA5EZbDrapEMQvDY9YGsATUOfX/+B2oq74m
DGKTuuiQyWkjALgZKM1xPZvqAOsOskaUB0gKAF2XSJZDasNteNTTrxV8aY+5Pz1v
l9E5Q3Y2wK+9UioB5QKInDghAWLlKxsW2XExWSVZ5TTzlH96VzcMBB0bCOKGzMDQ
8zrQIi8l19O/5K2uBlntYa5N4vAAML11Tjfzh+dXS1X9hFWxFXYz/7sjjtUyHENh
wS+a2gSjmw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,39 @@
-----BEGIN CERTIFICATE-----
MIIG2jCCBcKgAwIBAgIQNihkjkk3DjZQoPK9QiHrpjANBgkqhkiG9w0BAQsFADB1
MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzQxEzARBgNVBAoMCkFw
cGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTI1MDgwNDA3MzgyOFoXDTI2MDkwMzA3
MzgyN1owgbwxJTAjBgoJkiaJk/IsZAEBDBVjb20uamloZS5kaW1lbnNpb25odWIx
MzAxBgNVBAMMKkFwcGxlIFB1c2ggU2VydmljZXM6IGNvbS5qaWhlLmRpbWVuc2lv
bmh1YjETMBEGA1UECwwKUEYzUUc4MzdYUzE8MDoGA1UECgwzWWlsaW5nIEludGVy
YWN0aXZlIChCZWlqaW5nKSBUZWNobm9sb2dpZXMgQ28uLCBMdGQuMQswCQYDVQQG
EwJDTjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMYffzTSt+SRYZwv
KvRDJDxq+3MHGdZCjQY2rlOi9gpOrP0TGOczZhAcMoQfKRQ3oJrxmmxakoyOagvb
ma3MJwGvgfykCb3FWRtVPgBvge99kkLYvF/5g8gbC5ICpxNEaghY+Ou1EG2X8blB
zJfgki8Wvm0I72N/hGm0czRkKGSKqAJK82ep8exyRY32HeyAsxvaUG8KqXNa/hCg
uumMdx+G3DbMEDE2U3zHtl1g7+adG16B4fExxrxNDrBzpFK/UtQn9i9qVHShtn2h
LjrwGxB+SNIjCynv+Qw3iOCRuoXPWwFQWzt17H15X7Vxemv+uMJRYnv570ayOhUR
sNvPjYECAwEAAaOCAxwwggMYMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUW9n6
HeeaGgujmXYiUIY+kchbd6gwcAYIKwYBBQUHAQEEZDBiMC0GCCsGAQUFBzAChiFo
dHRwOi8vY2VydHMuYXBwbGUuY29tL3d3ZHJnNC5kZXIwMQYIKwYBBQUHMAGGJWh0
dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcmc0MDEwggEdBgNVHSAEggEU
MIIBEDCCAQwGCSqGSIb3Y2QFATCB/jCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5j
ZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2Vw
dGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQg
Y29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlm
aWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA2BggrBgEFBQcCARYqaHR0cHM6
Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5MBMGA1UdJQQMMAoG
CCsGAQUFBwMCMDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly9jcmwuYXBwbGUuY29t
L3d3ZHJnNC0xLmNybDAdBgNVHQ4EFgQUZc5UvLvguWHNeCnrjQa3q3Fio1YwDgYD
VR0PAQH/BAQDAgeAMIG3BgoqhkiG92NkBgMGBIGoMIGlDBVjb20uamloZS5kaW1l
bnNpb25odWIwBwwFdG9waWMMGmNvbS5qaWhlLmRpbWVuc2lvbmh1Yi52b2lwMAYM
BHZvaXAMImNvbS5qaWhlLmRpbWVuc2lvbmh1Yi5jb21wbGljYXRpb24wDgwMY29t
cGxpY2F0aW9uDB5jb20uamloZS5kaW1lbnNpb25odWIudm9pcC1wdHQwCwwJLnZv
aXAtcHR0MBAGCiqGSIb3Y2QGAwEEAgUAMBAGCiqGSIb3Y2QGAwIEAgUAMA0GCSqG
SIb3DQEBCwUAA4IBAQAeWCHl7OB0+qkX1lyof30KzwWK/qclwP1VlYfjmnwsuXUP
9PyXzNzt82T1gAJAy5ucC0degty+9sDdoM7Kc7zv+9byeplLAu+m+TkB3V6K47m4
Uedf9H5wl6X+Li9eUe9XvxiA08LI/9K2HqGUPSj/ePv9DFA4RNuELWxQ5xVjTfOX
xTt8wGnq2tTr0vS3tmnqhgXXGtv20AkfKrFLR4S5OBFMeEGZh6jYL3q+jLE2olU0
vrwFcvaYk/borkZ1opbij/8sCrpsPrPCIqY1+gsd+mF+lyVdlwZbHBtjr+BWEcIh
E9FRejTpAwW3+0QGk89meqQscMQkwFDEMaEFY/MX
-----END CERTIFICATE-----

View File

@ -1,32 +0,0 @@
Bag Attributes
friendlyName: nanjinjihe
localKeyID: DA 70 54 EB A0 6A B0 46 36 FE 3D 27 03 3F 74 CB C6 D0 CE 19
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyG+CvTAcfteBN
AzDoDaaltQ5Ynf3dMNwzVNx9LCeTMpKhDI1xwt4rUH7wPsOW4OCi2EfVHR8+fejs
7B4rsSiXmTYwusTqe5gut3e6h1CPsR7PHSOVEWnapXS7UbVfs2TRdw2Z9pQG5vlC
/mG7vjEipR62S6UkRJlJqkSCqVzXFfSMCjCTxXxAKOWjFMxVDuGgLqBoK09hf9Js
m+5clL8WykfFt/wRNeX9rGgi3+Gkols2UDLcKIg0xClQZLHfjDw8OOi+EQ2v4VHR
6fRpSIdIk/zIGUgp9U5PNQSd+Hxqx43o7RgRMCWVh/KrtU9DoxUYW4cWffNCYszY
ioNy1PZhAgMBAAECggEBAKHV3OHLhkmU/Dtd4+lmxf6ZtnBlFpAOXncx66q9HTSN
5lf5zOgKdc8LtFvd2+DTcqnlNdf6PzKMO8JThdKThdvB9lBu1JBvP0dro5nUbCo0
eVceFf/UM8fdQWUXiGNt4PTHZY95UuW2p9TsyadK2OCvAmoH8AU1Y8dgtCG5fEde
AqaMK6s6/Ib6K+dFbQPx3CjVcom7kC7sJ3pSHQJs4Ze14xy75jin0QC83YWNEOSR
gH2m8OVNF0Jum0pAOnlI0/MAW+L4uPrl+PG+OQQRc1Eq/6OiTQceLMoqHsjIF5UU
TNNxVZQ95PN4uHx1vQq6Vl8iXBxE5VYzSV5OvZA+pQECgYEA15lvZHRTLJVWMfqa
kvbp3mrx07nlaWh9Hv6Mpm8pY9WcvSNtrC7zGvhb0KaXEPh3M2k3FRcToSF3ECjT
D8bDtEzSgS/lBeXJkJdvmfxXjFDCG1+NrjAt2H54pSZV99lkn96fZlFRbTU2Y+Vb
Bx9f3VfJFN9ki/inMu+WG7tedvECgYEA03v6uDZoq2zEwIpOR1eW091Wgph8QUfs
7MJTZZryvE9lmMH6X2jMHwQjMGgzjOr5mLjJsc76W7r0EUvwuTkedCboOcHEB5oW
FmR4PCAFn1UmPGXb1JuSBjd4DcHMDZNgm9p7Hx8mi/hysca+LwQy+ugfs3VJy2Ot
xNmV27QF1nECgYA+ZKL9+UQQxO7668FMArTPrzdoxIWFqb17+MKPb+AUij8f5V7Z
04R7K6C2k19og99LO4m7SsBdgyfHv2jFSgpc5VaOGGQ4/rUEaS7c61ASxVRHQ7Hm
1P6Phpo3gQ6j+X/LXEvY+nOMHTguVLOxLf06OytwJO4eK8mHThqsz96YoQKBgQCN
56fxSZjsRfyzkVo7JruvB9rPoI/noeKJb09UYyczUjdCzA47TO+CdF1EUvccPGMk
pgG4jMCTIpw4qvMo5fnYSG8Uqj7mNu9RMTM4bOg45ILWjiZ2v7i48SleWcaY6WYu
zehFlrpfu2MChPACTlgC4hS7bx0IG6dy6elTVbWBEQKBgEX/6Uk1s9Sne6ksb/0b
/YcDKuDkeTM541+x8YoNe9VOG1hbnYFPmdrxNDzwLZws8ICdwdtfq3GFuBk3pAxp
kRmFNBaoekjvoLnUzPv/mpr/FGz7ZncnyU8d4b0ON+nOrxdt3tR8jOwlIVZkxpyl
kijLSqq+kIwhMfTZytJo57wS
-----END PRIVATE KEY-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDGH3800rfkkWGc
Lyr0QyQ8avtzBxnWQo0GNq5TovYKTqz9ExjnM2YQHDKEHykUN6Ca8ZpsWpKMjmoL
25mtzCcBr4H8pAm9xVkbVT4Ab4HvfZJC2Lxf+YPIGwuSAqcTRGoIWPjrtRBtl/G5
QcyX4JIvFr5tCO9jf4RptHM0ZChkiqgCSvNnqfHsckWN9h3sgLMb2lBvCqlzWv4Q
oLrpjHcfhtw2zBAxNlN8x7ZdYO/mnRtegeHxMca8TQ6wc6RSv1LUJ/YvalR0obZ9
oS468BsQfkjSIwsp7/kMN4jgkbqFz1sBUFs7dex9eV+1cXpr/rjCUWJ7+e9GsjoV
EbDbz42BAgMBAAECggEAf4755LWixlu//8Hyou+znd63ZDj3LpcMmyqLlLfAN6Su
BgbMqtbR6J9u+tfhJ+4kzn2tkgPChRKQPnA2AssFdjVcuJ+Y0gEEvO9mCvvAKcSs
bbODnTtV2HlpOqnQjjwnFJbMNV4PZLBGZqNxqXtiQ0Ued68gCeypATG4VswBobDt
7ynEBQgqksBJKGV+ZPu5YOJT21KbPx5FAXcRD+pgy4SnkQ1UEbNpBBeNsW0c7Uzw
8SclJnoiYzuvdSxu+PQFF35Eo1a/AflezWmtE//zFpuPMRMDyQuZlo2YHEcKYxcb
G7KxgclkXJk3wXARYGAdeaMHJsG/KHQnuoAaGr1QkQKBgQDrebKASF3cd1+iH7N6
u9NLN9EVXdKCvKjav49UDVOqTWYBjnz0w5TzA1V1Y/FK34pNmw2n6QAKORGdLDzA
AL6T0MZbqNGrqj6Y+AVAumewm9oWUsTg6IoZa7Wjc7r2cPXvarTf9eSkRqCN+3Fc
gC3GvGw/33TL75cEOcvriCrFRQKBgQDXZFWerEOyCWhOVHDjVAwzm21Z4MJDvXzi
8+bQV6jovfoUN6/HcpvI5EWVUaJgFcM7kJM1mWn3gCGpBmJRrtdcxail8Lgb4nPz
6k6ycO2evovn6tII6RVV3f79M+kjmcBkdGJb3A/Q5KvJjNzOwuvRPoD7NpHUtCk+
DiPPS791DQKBgQCIg1MSv+F6cNJGgS5iFwXV85QJWQ0zsgAKYUfMqLU6ZAihGmQi
2kg94k7m8XvvtkzN1PhDwfa99nvZrM3DWisiMsTsWqDDbr0/nXoeHnMCljgG/REa
enWlayYwjmVzfeV2WVxnAnz9J9zosz7zK3ReZNo05W0m1c2tK3ynHZf+KQKBgQCX
iYOjatcFvxCfa1Ew3sTm2gcJINQMDJh8o9RT6BhyBpaW+sa0r7FimRdLgm+W4jjF
A2ZOY2zmMEN+Q3uQtE3Ye0e316/wDlY92S8c486JzM1pkQ/MLRBRpSnswkTYQyfU
hP1eW2jVMsyZtHz1CrYWNL9R/oaC+224twICqew9UQKBgHk6D7ClNz8gCKrmjnvU
81PmQXOIfUfikzk4YR3cixUiyYXbCV611SGocGj6lleABrUAttCGGghn7Zletons
FY3gj+SBMYveN9KYYq2Ox0i77kQ1cwseYxA3aPs5wIQmRGl7oEtK68/kLhn7DT7k
8dC+avgB/Ih9C4pNJj3ZgkrI
-----END PRIVATE KEY-----

View File

@ -86,8 +86,14 @@ read_body(Req, AccData) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
handle_request("POST", "/api/prod_push", _, Notifications) ->
{ok, Pid} = dimension_apn_worker:start_link(apns_prod),
dimension_apn_worker:push(Pid, Notifications),
{ok, 200, dimension_utils:json_data(<<"success">>)};
handle_request("POST", "/api/push", _, Notifications) ->
{ok, Pid} = dimension_apn_worker:start_link(),
{ok, Pid} = dimension_apn_worker:start_link(apns_dev),
dimension_apn_worker:push(Pid, Notifications),
{ok, 200, dimension_utils:json_data(<<"success">>)};

View File

@ -12,7 +12,7 @@
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([start_link/1]).
-export([push/2]).
-export([test/0]).
@ -31,7 +31,7 @@
%%%===================================================================
test() ->
{ok, Pid} = start_link(),
{ok, Pid} = start_link(apns_dev),
DeviceToken = <<"b48b911e85874b403ce80cbb33864e8ed6f06455e80310b0f6b95e672a3e39dc">>,
Title = <<"动物狂响曲"/utf8>>,
Body = <<"第7集(校服与被毛更深处),bilibili已更新"/utf8>>,
@ -54,10 +54,10 @@ push(Pid, Notifications) when is_pid(Pid), is_list(Notifications) ->
gen_server:cast(Pid, {push, Notifications}).
%% @doc Spawns the server and registers the local name (unique)
-spec(start_link() ->
-spec(start_link(Profile :: atom()) ->
{ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
gen_server:start_link(?MODULE, [], []).
start_link(Profile) when is_atom(Profile) ->
gen_server:start_link(?MODULE, [Profile], []).
%%%===================================================================
%%% gen_server callbacks
@ -68,8 +68,8 @@ start_link() ->
-spec(init(Args :: term()) ->
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
{stop, Reason :: term()} | ignore).
init([]) ->
{ok, Props} = application:get_env(dimension_apn, apns),
init([Profile]) ->
{ok, Props} = application:get_env(dimension_apn, Profile),
ConnectionOpts0 = proplists:get_value(connection_opts, Props),
Headers0 = proplists:get_value(headers, Props),

View File

@ -10,12 +10,29 @@
]},
%% 推送配置
{apns, [
{apns_dev, [
{connection_opts, [
{apple_host, "api.sandbox.push.apple.com"},
{apple_port, 443},
{certfile, "aps_development.pem"},
{keyfile, "priv_key.pem"},
{certfile, "cert.pem"},
{keyfile, "private.key"},
{type, cert},
{timeout, 5000}
]},
{headers, [
{apns_expiration, 0},
{apns_priority, 10},
{apns_topic, "com.jihe.dimensionhub"},
{apns_push_type, "alert"}
]}
]},
{apns_prod, [
{connection_opts, [
{apple_host, "api.push.apple.com"},
{apple_port, 443},
{certfile, "cert.pem"},
{keyfile, "private.key"},
{type, cert},
{timeout, 5000}
]},

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
img_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB