背景:一个记录表,类似日志的信息,查询大量集中在某个用户个人的数据,分区需要尽量保证一个人的数据在一个分区里。因此采用通过user_id进行hash分区的方式。
作者: ianzhi
-
软著申请
软著申请在中国版权保护中心,记录一下具体的要求细节,更具体的信息参考:计算机软件著作权登记指南。
-
函数计算WordPress实例预热程序
函数计算有冷启动的问题,如果启用VPC,还有VPC的启动时间,为了避免冷启动问题,可以通过预热程序来避免函数实例被销毁。此预热程序示例使用Cloudflare Worker,正好同时用预热程序支持Wordpress的定时任务。
-
使用Caddy反向代理WordPress
具体配置
xxx.xxxx.xxx { tls youremail@yourmailserver root * /path/to/wordpress # GZIP和FPM配置 encode gzip file_server php_fastcgi php:9000 # 静态文件配置 @static_files { path_regexp .(?:css|js|woff2?|svg|gif|map|png|jpg|webp|gif|jpeg|mp4|mp3|wav|mov|heic) } header @static_files { Cache-Control "public, max-age=15778463" X-Robots-Tag "none" X-Permitted-Cross-Domain-Policies "none" X-Frame-Options "SAMEORIGIN" X-Download-Options "noopen" X-Content-Type-Options "nosniff" # Referrer-Policy "no-referrer" } # 禁止访问的目录/文件 @disallowed { #path /wp-cron.php #path /xmlrpc.php path *.sql path /wp-content/uploads/*.php path /wp-content/uploads/*.html path /wp-content/debug.log } rewrite @disallowed =404 }
-
Linux配置SWAP交换分区
1. 创建交换文件
dd if=/dev/zero of=/swap bs=1G count=2 # 如果出现Killed,一般是可用内存不足,可以尝试以下 dd if=/dev/zero of=/swap bs=1M count=2048
2. 启用SWAP文件
mkswap /swap swapon /swap
3. 配置开机自动挂在
echo '/swap none swap defualts 0 0' >> /etc/fstab
4. 配置优先使用内存
echo 'vm.swappiness=0'>> /etc/sysctl.conf sysctl -p
-
Debian配置自动清理Journal日志
在Debian系统上(红帽系Linux发行版应该也是一样的),
systemd-journald
服务负责管理journal
日志。这些日志可以占用大量的磁盘空间,特别是当系统持续运行并且产生大量日志条目时。可以配置systemd-journald
的日志保留策略来自动清理这些日志。 -
MySQL配置及优化
一、配置项及意义
配置项 意义 建议 innodb_buffer_pool_size 控制 InnoDB 存储引擎的内存缓存池大小 总内存(非专用服务器可以使用希望分配给数据库的内存量)的 50% 到 80% innodb_buffer_pool_chunk_size InnoDB 缓冲池大小调整操作的块大小,默认值为 128MB innodb_buffer_pool_size = innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances innodb_buffer_pool_instances 决定了 InnoDB 缓冲池(Buffer Pool)的实例数量 设置为 CPU 核心数的较小倍数,超过 1GB 时可以适量增加,最高不超过1000 innodb_buffer_pool_in_core_file 从核心文件中排除缓冲池页面,参考15.8.3.7 从核心文件中排除缓冲池页面_MySQL 8.0 参考手册 off innodb_flush_log_at_trx_commit 定义了事务提交时,InnoDB 如何处理未刷入(flush)的重做日志(redo log),默认值为1,设置为0/2,在操作系统崩溃或断电时可能会丢失最后一秒的事务数据 对于需要高可靠性和数据完整性的系统设置为 1,对于性能要求更高,且可以接受一定数据丢失风险的系统设置为 0 或 2。 innodb_change_buffering 控制着 InnoDB 执行变更缓冲(Change Buffering)的程度 none innodb_log_buffer_size 设置InnoDB日志缓冲区的大小 64m innodb_flush_method 决定了 InnoDB 如何将数据和日志刷新(flush)到磁盘 O_DIRECT innodb_purge_threads 定义了用于回收不再需要的 UNDO 日志的 Purge 线程的数量 cpu > 4 ? 4 : 1 -
APP Store应用上架需要注意的点
最近公司的应用上架APP Store过程中一直遇到审核问题,记录一下遇到的问题,方便后续其他应用开发避坑。
## 1、与安卓通用的审核条件
>>> [APP分发前的准备工作](https://www.dnote.cn/posts/899)
## 2、虚拟支付
– 所有的虚拟商品直接使用苹果的内购方式付款,避免后续改造的问题(需要给苹果30%抽成)
– 提前考虑宣传物料和价格在不同货币区域的展示,或者避免物料中包含价格
– 至少审核阶段,不要在应用界面里放跳转到其他应用的链接,苹果审核会认为这涉及到其他支付方式## 3、强制登录问题
微信小程序也是一样的要求,所以最好设计阶段就开始考虑这个问题。– 不需要用户信息的数据不设计为强制鉴权
– 将引导用户登录放到用户进行必须鉴权的操作时 -
Linux配置Swappiness
1. 调整交换分区使用配置
vim /etc/sysctl.conf vm.swappiness = 40
2. 让配置生效
sysctl -p
-
使用Docker Compose部署NextCloud和WordPress
一、全局配置
name: lnmp services: caddy: image: caddy:latest volumes: - ./www:/var/www/html - ./caddy/etc:/etc/caddy - ./caddy/data:/data - ./caddy/config:/config ports: - 80:80 - 443:443/tcp - 443:443/udp logging: driver: "json-file" options: max-size: "10m" max-file: 3 restart: always redis: image: redis:latest volumes: - ./redis/config:/etc/redis - ./redis/data:/data restart: always command: /etc/redis/redis.conf mysql: image: mysql:latest volumes: - ./mysql/config:/etc/mysql - ./mysql/data:/var/lib/mysql - ./mysql/mysql-files:/var/lib/mysql-files cap_add: - SYS_NICE security_opt: - seccomp:unconfined environment: MYSQL_ROOT_PASSWORD: password ports: - 3306:3306 restart: always php: build: ./php volumes: - ./php/config:/usr/local/etc - ./php/logs:/var/log/php - ./www:/var/www/html depends_on: - caddy - mysql - redis cap_add: - SYS_PTRACE logging: driver: "json-file" options: max-size: "10m" max-file: 3 restart: always imaginary: image: nextcloud/aio-imaginary:latest restart: always command: -concurrency 2 -enable-url-source environment: - PORT=9000
1. PHP
PHP官方的镜像启用和安装的扩展比较少,直接使用会导致WordPress和Nextcloud的健康检查一堆信息,所以使用Dockerfile来基于官方镜像构建一个专用的镜像,PHP需要的扩展包括:
- gd(png/jpeg/gif/webp/avif)
- imagick
- opcache(考虑性能)
- apcu(Nextcloud的本地缓存)
- zip(影响WordPress插件安装)
- redis(WordPress的对象缓存和Nextcloud的分布式缓存)
- gmp
- intl
FROM registry.cn-beijing.aliyuncs.com/ianzhi/php:8.4-fpm-alpine # 配置国内镜像 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories # 安装构建扩展相关依赖 RUN apk add --no-cache --update --virtual .build-deps $PHPIZE_DEPS # MySQL RUN docker-php-ext-install pdo_mysql mysqli \ && docker-php-ext-enable pdo_mysql mysqli # 常用扩展 RUN docker-php-ext-install pcntl exif bcmath sysvsem \ && docker-php-ext-enable opcache exif bcmath pcntl sysvsem # apcu RUN pecl install apcu && docker-php-ext-enable apcu # zip扩展 RUN apk add --no-cache --update libzip=1.11.4-r0 libzip-dev=1.11.4-r0 unzip \ && docker-php-ext-install zip \ && docker-php-ext-enable zip # redis RUN pecl install https://pecl.php.net/get/redis-6.2.0.tgz \ && docker-php-ext-enable redis # intl RUN apk add --no-cache --update icu icu-dev \ && docker-php-ext-configure intl \ && docker-php-ext-install intl \ && docker-php-ext-enable intl # imagick RUN apk add --no-cache --update imagemagick-dev imagemagick-svg \ && pecl install https://pecl.php.net/get/imagick-3.8.0.tgz \ && docker-php-ext-enable imagick # gd RUN apk add --no-cache --update libpng libpng-dev libavif-dev libjpeg-turbo-dev freetype-dev freetype libjpeg-turbo libavif \ && docker-php-ext-configure gd --with-freetype --with-jpeg --with-avif \ && docker-php-ext-install gd \ && docker-php-ext-enable gd # ffmpeg nextcloud需要视频转码时启用 # RUN apk add --no-cache ffmpeg # gmp nextcloud使用加密时使用 # RUN apk add --no-cache --update gmp-dev \ # && docker-php-ext-install gmp \ # && docker-php-ext-enable gmp # pgsql # RUN apk add --no-cache --update libpq-dev postgresql-dev \ # && docker-php-ext-install pdo_pgsql \ # && docker-php-ext-enable pdo_pgsql # 删除构建依赖 RUN apk del --no-network .build-deps # 配置文件 RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
-
《老姚的底层逻辑认知课》笔记
1. 六个经济变化
未来的六大经济环境变化:
通缩转为通胀
利率由负转正
逆全球化
基础能源资料暴涨
生产成本提高
赚钱效益减弱2. 主要股指进出点位评估
股指 参考指数 低点 高点 卖出点位 周期 A股 沪深300市盈率中位数 16-19 40 30-33 4年左右 港股 恒生指数市盈率 10 20 16-17 5-6年左右 美股 标普500市盈率 12-14 27-30 27-30 10年 -
apache跨域配置
用Apache比较少,今天正好碰到,记录一下,本质就是修改响应头信息。
1. 取消注释主配置文件中的下面这行
# 加载模块 # LoadModule headers_module modules/mod_headers.so
2. 修改vhost配置
<Directory /www/web/yuming.com/public_html/> Options FollowSymLinks AllowOverride All Require all granted Header set Access-Control-Allow-Origin * # 或者指定具体的主机名 # Header set Access-Control-Allow-Origin http://example.com # Header set Access-Control-Allow-Origin http://localhost:8080 </Directory>
-
在Chrome扩展中使用Parse Platform-集成Gitee登录
国内提供Oauth认证服务的好少,使用第三方登录降低了认证服务的复杂度,也降低了用户的决策难度,这里用Gitee作为一个示例。应该适用于大多数的Web应用,只有客户端获取authorization_code部分的代码可能不一致。Parse官方文档里的自定义认证写的语焉不详,折腾了好久。
服务端1. Parse Server配置认证信息“`typescript
const gitee = require(‘./auth/gitee’);{
auth: {
gitee: {
module: gitee, // OR object,
client_id: “”, // Gitee第三方应用ID
client_secret: “”, // Gitee第三方应用的密钥
}
}
}
“`2. 实现自定义登录(AuthAdapter)
主要需要实现两个函数:validateAuthData和validateAppId。
const qs = require('querystring') const Parse = require('parse/node') async function getAuthData(code, redirect_uri, options) { const token = await codeToToken(code, redirect_uri, options) const user = await userInfo(token.token_type, token.access_token) return { id: user.id, access_token: token.access_token } } async function codeToToken(code, redirect_uri, options) { const url = `https://gitee.com/oauth/token` const query = qs.stringify({ grant_type: 'authorization_code', code, redirect_uri, client_id: options.client_id, client_secret: options.client_secret }) const res = await fetch(`${url}?${query}`, { method: 'POST' }) return await res.json() } async function userInfo(type, token) { const url = 'https://gitee.com/api/v5/user' const res = await fetch(url, { method: 'GET', headers: { Authorization: `${type} ${token}` } }) return await res.json() } // Returns a promise that fulfills if this user id is valid. async function validateAuthData(authData, options) { if (!authData.id) { throw new Parse.Error(Parse.Error.OTHER_CAUSE, '缺少id字段'); } if (!authData.access_token) { throw new Parse.Error(Parse.Error.OTHER_CAUSE, '缺少access_token字段'); } const user = await userInfo('bearer', authData.access_token) if (user && String(user.id) === String(authData.id)) { return } throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'authData is invalid for this user.'); } // Returns a promise that fulfills iff this app id is valid. function validateAppId(appIds, authData, options) { console.log('validateAppId:', appIds, authData, options) return Promise.resolve(); } module.exports = { validateAuthData, validateAppId, getAuthData }
3. 添加一个云函数,用于获取authData
const gitee = require('../auth/gitee') const config = require('./config') Parse.Cloud.define("giteeAuthData", async (request) => { return await gitee.getAuthData(request.params.code, request.params.redirect_uri, config.auth.gitee) })
客户端
async function login() { const redirectUrl = chrome.identity.getRedirectURL() logger.log('redirect_url:', redirectUrl) // 获取认证码 const authUrl = await _oauthCode(redirectUrl) if (authUrl === undefined) { throw new Error('login failed') } const url = new URL(authUrl) const code = url.searchParams.get('code') if (code == null) { throw new Error('login failed') } logger.log('code:', code) // 获取认证信息 const authData = await _oauthData(code, redirectUrl) user.value = await Parse.User.logInWith('gitee', { authData }) } async function _oauthData(code: string, redirect_uri: string) { const authData = await Parse.Cloud.run('giteeAuthData', { code, redirect_uri }) logger.log('authData:', authData) return authData }
参考
[1] Parse 自定义认证文档地址, Parse Server Guide | Parse (parseplatform.org)
[2] Gitee Oauth文档,Gitee OAuth 文档
-
在Chrome扩展中使用Parse Platform-邮箱验证码登录
本来想使用oauth来实现登录,但是国内提供oauth服务要么比较小众,要么居然收费的?传统的用户注册登录使用起来过于繁琐了,很容易把用户挡在最开始的地方,最后决定添加邮箱验证码登录。
Parse Platform的文档中提到了一个
Parse.User.become()
方法,但是需要传递一个session token进去,翻了很多遍文档也没发现该如何获取这个session_token,最后在Github仓库的issue中搜到2023年Parse增加了一个loginAs
方法,可以通过传递一个userId来将用户登陆进系统,那么通过云函数和Parse.User.become()
就可以实现邮箱验证码登录了。服务端
1. 创建发送验证码函数
const VerifyCode = Parse.Object.extend('VerifyCode') /** * 发送邮件验证码 */ Parse.Cloud.define('verifyCode', async (request) => { const email = request.params.email const code = generateRandomString(6) // 记录验证码 const verifyCode = new VerifyCode() verifyCode.set('email', email) verifyCode.set('code', code) verifyCode.set('expiredAt', new Date(Date.now() + 1000 * 60 * 5)) await verifyCode.save(null, { useMasterKey: true }) // 删除过期的验证码记录 const query = new Parse.Query(VerifyCode) query.lessThan('expiredAt', new Date()) await Parse.Object.destroyAll(await query.find({ useMasterKey: true }), { useMasterKey: true }) // 发送邮件 await Parse.Cloud.sendEmail({ templateName: "verifyCode", placeholders: { code }, recipient: email }); }, { fields: { email: { required: true, type: String } }, requireUser: false })
2. 创建登录函数
const VerifyCode = Parse.Object.extend('VerifyCode') Parse.Cloud.define('login', async (request) => { if (!request.params.email) { throw new Parse.Error(Parse.Error.OTHER_CAUSE, '缺少email字段'); } if (!request.params.code) { throw new Parse.Error(Parse.Error.OTHER_CAUSE, '缺少code字段'); } const query = new Parse.Query(Parse.User) query.equalTo('email', request.params.email) let user = await query.first({ useMasterKey: true }) if (user === undefined) { throw new Parse.Error(Parse.Error.OTHER_CAUSE, '用户不存在'); } // 验证 const codeQuery = new Parse.Query(VerifyCode) codeQuery.equalTo('email', request.params.email) codeQuery.equalTo('code', request.params.code) codeQuery.greaterThan('expiredAt', new Date()) const code = await codeQuery.first({ useMasterKey: true }) if (code === undefined) { throw new Parse.Error(Parse.Error.OTHER_CAUSE, '验证码已过期'); } // 修改邮箱验证字段为验证通过状态 user.set('emailVerified', true) user.save(null, { useMasterKey: true }) // 登录用户 user = await Parse.User.loginAs(user.id) return user.getSessionToken() }, { fields: { email: { required: true, type: String }, code: { required: true, type: String } } })
客户端
export async function emailLogin(email: string, code: string) { const token = await Parse.Cloud.run('login', { email, code }) return await Parse.User.become(token) }
参考
[1] 设置当前用户,JavaScript 开发人员指南 |解析 (parseplatform.org)
[3] Parse Server API Mail Adapter,parse-server-api-mail-adapter – npm (npmjs.com)