## [柳暗花明下一跳](/ip-route)
讲实话, 你知道这个ip包的下一跳在哪么?
思考三秒.
之前的客户端一直是win和mac, 让我错误的陷入到路由表很简单, 目的地, 掩码, 下一跳.
后面阿里云推了200m的轻量, 我就在想, 一个没有公网的ecs一个没有性能的轻量, 这轻量不就是这个ecs的网卡么.
- ip rule
- iptables
- ip route
---
未完待续
## [私人网盘秒变对象储存](/onedrive-api-proxy)
有一说一, 网盘加上代理等于对象存储么?
思考三秒.
对象储存对企业而言相对于磁盘储存数据库等可能很便宜, 但是对个人来说相对于国内内卷的网盘还是很贵. 当然缺点就是强依赖于各个网盘的实现, 靠提供商保证可用性. 同时也没有像对象存储一样有主流的S3 api支持, 比较有名的是WebDAV, 但是无论国内国外都只有少部分支持, 大厂网盘完全各玩各的, 无论是客户端还是接口实现上百花齐放, 但是总得来说最总还是支持上传和下载的.
OneDrive是巨硬家的网盘, 捆绑到Office365销售. 免费版享受5/15GB空间, 升级到Basic支持100GB, 升级到Personal/Family支持1TB每个人.
OneDrive的api的长什么样子, 能否代理成为对象存储呢?
---
### 认证
认证类似于用户登录, 最简单的登录方式比如输入账号密码, 但是OneDrive这里用了另一种方式访问令牌.
同样的也需要先输入账号和密码登录获取令牌, 之后可以使用令牌访问全部服务.
账号和密码并不是你的云盘账号密码, 而是一个客户端的账号和密码, 默认是没有的, 需要你先去[注册客户端](https://aka.ms/AppRegistrations/?referrer=https%3A%2F%2Fdev.onedrive.com).
如果你有Azure服务直接注册就行了, 注册需要验证信用卡, 没有Azure服务选择我是Office365开发者.
之后我们申请到一个客户端ClientID, 还需要为这个客户端生成一个密钥Secret. 微软的权限体系分的比较细, 所以流程上看起来是有些繁琐.
申请到的这个[客户端的权限](https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#authentication-scopes)是空的, 我们需要的两个权限为`offline_access`和`files.readwrite.all`, 除了官方的说明, 第一个可以理解为客户端获得令牌的失效时间为无限(需要主动去续签), 第二个为对OneDrive的读写.
同时我们还需要一个服务器和域名, 没有的可以和我一样用[CloudFlare Worker](https://workers.cloudflare.com/)提供的域名和服务. 回调配置为[`{appname}.{subdomain}.workers.dev/auth-redirect`](https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#response-1)即可.
这套繁琐的流程就算结束了. 接下来代码的实现我用的是javascript的fetchAPI, 其他语言一样的.

- 获取授权码
- 获取访问令牌和刷新令牌
按照以上步骤来就可以.
我们在浏览器直接发起`https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id={ClientID}&scope=offline_access%20Files.ReadWrite.All&response_type=code&redirect_uri=https://{appname}.{subdomain}.workers.dev/auth-redirect`
看这里他需要我们登录OneDrive给这个Client授权之前说的两个权限, 之后重新请求就没有了. 会按照你配置的回调地址302带上一个`code`参数, 这就是授权码, 第一步完成.
接下来编写服务端代码, 逻辑为
- 如果请求的域名和回调域名不一致返回
- 拿着授权码请求令牌服务
- 获取访问令牌和刷新令牌存入缓存
```js
{
client_id: `xxx-xx-xx-xx-xxx`,
scope: [`offline_access`, `Files.ReadWrite.All`],
client_secret: `xx~xxx~xx~xx`,
redirect_uri: `https://{appname}.{subdomain}.workers.dev/auth-redirect`,
async auth(grant_type, grant_value) {
const rsp = await fetch(`https://login.microsoftonline.com/consumers/oauth2/v2.0/token`, {
method: `POST`, headers: { [`Content-Type`]: `application/x-www-form-urlencoded` },
body: new URLSearchParams({
[`client_id`]: `${this.client_id}`,
[`scope`]: `${this.scope.join(` `)}`,
[`redirect_uri`]: `${this.redirect_uri}`,
[`client_secret`]: `${this.client_secret}`,
[`grant_type`]: `${grant_type}`,
[`${grant_type === 'authorization_code' ? 'code' : grant_type}`]: `${grant_value}`,
}).toString()
})
return rsp.json()
}
async fetch(request, env, ctx) {
const url = new URL(request.url)
if (`${url.origin}${url.pathname}` == `${this.redirect_uri}` && `${request.method}` === `GET`) {
const { access_token, refresh_token } = await this.auth(`authorization_code`, new URLSearchParams(url.search).get(`code`))
await env.cache.put(`access_token`, access_token)
await env.cache.put(`refresh_token`, refresh_token)
return Response.redirect(`${url.origin}`, 302)
}
}
}
```
除了`access_token`,`refresh_token`响应中还有令牌过期时间, 我这里是3600秒, 也就是一个小时, 我们需要一个更小的间隔时间去刷新令牌并编写刷新令牌代码.
```js
{
async scheduled(event, env, ctx) { // cron: */30 * * * *
const { access_token, refresh_token } = await this.auth(`refresh_token`, await env.cache.get(`refresh_token`))
await env.cache.put(`access_token`, access_token)
await env.cache.put(`refresh_token`, refresh_token)
},
}
```
### 上传
请求的路径就是网盘的路径, 需要提前对这个路径做下修正, 防止请求到空目录如`//`这种情况.
```js
{
based_path: `/.sites`, // 如果你只想暴露子文件夹
async fetch(request, env, ctx) {
...
const url = new URL(request.url)
const url_path = `/${url.pathname.split('/').filter(e => e).join('/')}`
const item_path = `${this.based_path.substring(1)}${url_path.substring(1) ? url_path : ``}`
...
}
}
```
这个`item_path`为[文档定义的访问路径](https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/api/driveitem_get?view=odsp-graph-online#http-request).
接下来要把上传的PUT到对应的接口中.
```js
async fetch(request, env, ctx) {
...
if (`${request.method}` === `PUT`) {
const rsp = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:/${item_path}:/content`, {
method: request.method, headers: new Headers({ ...request.headers, [`Authorization`]: `Bearer ${access_token}` }),
body: request.body
})
const body = await rsp.json()
return new Response(`${JSON.stringify(body)}`, { status: rsp.status, headers: new Headers(rsp.headers), })
}
...
}
```
### 下载
因为要区分文件和文件夹, 下载这部分要比上传复杂点, 整理了下逻辑.
- 文件找不到返回
- 文件找到且是文件夹返回文件夹
- 文件找到且大于1M返回302到下载链接
- 文件找到下载文件并返回
```js
async fetch(request, env, ctx) {
...
// https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/api/driveitem_list_children?view=odsp-graph-online#list-children-of-a-driveitem-with-a-known-path
const p1 = fetch(`https://graph.microsoft.com/v1.0/me/drive/root:/${item_path}:/children?${url.search}`, { headers: { [`Authorization`]: `Bearer ${access_token}`, } })
// https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/api/driveitem_get?view=odsp-graph-online#http-request
const p2 = fetch(`https://graph.microsoft.com/v1.0/me/drive/root:/${item_path}?${url.search}`, { headers: { [`Authorization`]: `Bearer ${access_token}`, } })
const rsp2 = await p2
const body2 = await rsp2.json()
if (body2[`error`]) return new Response(`${JSON.stringify(body2[`error`])}`, { status: rsp2.status, headers: new Headers(rsp2.headers), }) // error
if (body2[`folder`]) return new Response(`${JSON.stringify(await (await p1).json())}`, { status: rsp2.status, headers: new Headers(rsp2.headers), }) // success
if (body2[`downloadUrl`] && body2[`size`] > 1 * 1024 * 1024) return Response.redirect(body2[`downloadUrl`])
const b = await fetch(body2[`downloadUrl`])
return new Response(await b.blob(), { headers: new Headers({ "Content-Type": body[`file`][`mimeType`], }), })
...
}
```
因为软子api可能会访问不通, 所以这里一定要做一个取舍, 那些请求可以在服务器下载后再返回, 那些请求需要直接转发.
我这里限制的为1M以上的转发, 1M以下的下载返回(2M). 这取决于CloudFlare到OneDrive的秒速度到底有多快. 如果1s内可以下载好并返回我认为是可以接受的. 不过这个值不准但是偏大但不会小了.
至此
## 日内交易策略[/trade-strategy]
分享一个日内交易策略, 基于量价和统计学的正态分布模型.
下图为2024年整年的收益率, 针对于三种有代表性的标的.
- 双汇发展(基准收益+2.6%)
- 长江电力(基准收益+30.1%)
- 招商银行(基准收益+49.78%)

---
策略分别评测三只有代表性的标的.
换手佣金为万二, 印花税为万五, 开多初始金额为一万.

观察资金曲线和k线对比, 慢涨时收益比较稳定, 慢跌时回撤抗造, 但是在九月底政策刺激下, 出现了比较大的剪刀差, 表现不佳.