fix: 修复配额说明重复和undefined问题

- 在editStorageForm中初始化oss_storage_quota_value和oss_quota_unit
- 删除重复的旧配额说明块,保留新的当前配额设置显示

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 19:39:53 +08:00
commit 4350113979
7649 changed files with 897277 additions and 0 deletions

35
backend/node_modules/svg-captcha/HISTORY.md generated vendored Normal file
View File

@@ -0,0 +1,35 @@
1.4.0 / 2019-05-03
===================
* Fixed #33 https://github.com/lemonce/svg-captcha/pull/33
1.3.11 / 2017-08-23
===================
* Fix typescript definition
1.3.9 / 2017-07-23
===================
* Fix charSet options
1.3.8 / 2017-07-20
===================
* Bump `opentype.js@0.7.3`
1.3.7 / 2017-06-11
===================
* Bump `opentype.js@0.7.2`
1.3.6 / 2017-06-03
===================
* Improve security by randomize svg path command
1.3.5 / 2017-05-08
===================
* Bump `opentype.js@0.7.1`
* Fixed some code style inconsistence

21
backend/node_modules/svg-captcha/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 - present Weilin Shi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

123
backend/node_modules/svg-captcha/README.md generated vendored Normal file
View File

@@ -0,0 +1,123 @@
![svg-captcha](media/header.png)
<div align="center">
[![Build Status](https://img.shields.io/travis/lemonce/svg-captcha/master.svg?style=flat-square)](https://travis-ci.org/lemonce/svg-captcha)
[![NPM Version](https://img.shields.io/npm/v/svg-captcha.svg?style=flat-square)](https://www.npmjs.com/package/svg-captcha)
[![NPM Downloads](https://img.shields.io/npm/dm/svg-captcha.svg?style=flat-square)](https://www.npmjs.com/package/svg-captcha)
</div>
> generate svg captcha in node.js
## Translations
[中文](README_CN.md)
## useful if you
- cannot or do not want to use google recaptcha
- have issue with install c++ addon
## install
```
npm install --save svg-captcha
```
## usage
```Javascript
var svgCaptcha = require('svg-captcha');
var captcha = svgCaptcha.create();
console.log(captcha);
// {data: '<svg.../svg>', text: 'abcd'}
```
with express
```Javascript
var svgCaptcha = require('svg-captcha');
app.get('/captcha', function (req, res) {
var captcha = svgCaptcha.create();
req.session.captcha = captcha.text;
res.type('svg');
res.status(200).send(captcha.data);
});
```
## API
#### `svgCaptcha.create(options)`
If no option is passed, you will get a random string of four characters and corresponding svg.
* `size`: 4 // size of random string
* `ignoreChars`: '0o1i' // filter out some characters like 0o1i
* `noise`: 1 // number of noise lines
* `color`: true // characters will have distinct colors instead of grey, true if background option is set
* `background`: '#cc9966' // background color of the svg image
This function returns an object that has the following property:
* `data`: string // svg path data
* `text`: string // captcha text
#### `svgCaptcha.createMathExpr(options)`
Similar to create api, you have the above options plus 3 additional:
* `mathMin`: 1 // the minimum value the math expression can be
* `mathMax`: 9 // the maximum value the math expression can be
* `mathOperator`: + // The operator to use, `+`, `-` or `+-` (for random `+` or `-`)
This function returns an object that has the following property:
* `data`: string // svg of the math expression
* `text`: string // the answer of the math expression
#### `svgCaptcha.loadFont(url)`
Load your own font and override the default font.
* `url`: string // path to your font
This api is a wrapper around loadFont api of opentype.js.
Your may need experiment around various options to make your own font accessible.
See the following api.
#### `svgCaptcha.options`
Gain access to global setting object.
It is used for create and createMathExpr api as the default options.
In addition to size, noise, color, and background, you can also set the following property:
* `width`: number // width of captcha
* `height`: number // height of captcha
* `fontSize`: number // captcha text size
* `charPreset`: string // random character preset
#### `svgCaptcha.randomText([size|options])`
return a random string.
#### `svgCaptcha(text, options)`
return a svg captcha based on text provided.
In pre 1.1.0 version you have to call these two functions,
now you can call create() to save some key strokes ;).
## sample image
default captcha image:
![image](media/example.png)
math expression image with color options:
![image2](media/example-2.png)
## why use svg?
It does not require any c++ addon.
The result image is smaller than jpeg image.
> This has to be a joke. /\<text.+\>;.+\<\/text\>/g.test...
svg captcha uses opentype.js underneath, which means that there is no
'&lt;text&gt;1234&lt;/text&gt;'.
You get
'&lt;path fill="#444" d="M104.83 19.74L107.85 19.74L112 33.56L116.13 19.74L119.15 19.74L113.48 36.85...'
instead.
Even though you can write a program that convert svg to png, svg captcha has done its job
—— make captcha recognition harder
## License
[MIT](LICENSE.md)

109
backend/node_modules/svg-captcha/README_CN.md generated vendored Normal file
View File

@@ -0,0 +1,109 @@
![svg-captcha](media/header.png)
<div align="center">
[![Build Status](https://img.shields.io/travis/lemonce/svg-captcha/master.svg?style=flat-square)](https://travis-ci.org/lemonce/svg-captcha)
[![NPM Version](https://img.shields.io/npm/v/svg-captcha.svg?style=flat-square)](https://www.npmjs.com/package/svg-captcha)
[![NPM Downloads](https://img.shields.io/npm/dm/svg-captcha.svg?style=flat-square)](https://www.npmjs.com/package/svg-captcha)
</div>
> 在node.js中生成svg格式的验证码
## Translations
[English](README.md)
## 什么情况下使用SVG验证码
- 无法使用 google recaptcha
- 无法安装 c++ 模块
## 安装
```
npm install --save svg-captcha
```
## 使用方法
```js
var svgCaptcha = require('svg-captcha');
var c = svgCaptcha.create();
console.log(c);
// {data: '<svg.../svg>', text: 'abcd'}
```
在 express中使用
```Javascript
var svgCaptcha = require('svg-captcha');
app.get('/captcha', function (req, res) {
var captcha = svgCaptcha.create();
req.session.captcha = captcha.text;
res.type('svg');
res.status(200).send(captcha.data);
});
```
## API
#### `svgCaptcha.create(options)`
如果没有任何参数,则生成的 svg 图片有4个字符。
* `size`: 4 // 验证码长度
* `ignoreChars`: '0o1i' // 验证码字符中排除 0o1i
* `noise`: 1 // 干扰线条的数量
* `color`: true // 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
* `background`: '#cc9966' // 验证码图片背景颜色
该函数返回的对象拥有以下属性
* `data`: string // svg 路径
* `text`: string // 验证码文字
#### `svgCaptcha.createMathExpr(options)`
和前面的 api 的参数和返回值都一样。不同的是这个 api 生成的 svg 是一个算数式,而
text 属性上是算数式的结果。不过用法和之前是完全一样的。
#### `svgCaptcha.loadFont(url)`
加载字体,覆盖内置的字体。
* `url`: string // 字体文件存放路径
该接口会调用opentype.js同名的接口。
你可能需要更改一些配置才能让你得字体好看。
详见下面的这个接口:
#### `svgCaptcha.options`
这是全局配置对象。
create和createMathExpr接口的默认配置就是使用的这个对象。
除了 size, noise, color, 和 background 之外,你还可以修改以下属性:
* `width`: number // width of captcha
* `height`: number // height of captcha
* `fontSize`: number // captcha text size
* `charPreset`: string // random character preset
#### `svgCaptcha.randomText([size|options])`
返回随机字符串
#### `svgCaptcha(text, options)`
返回基于text参数生成得svg路径
在 1.1.0 版本之前你需要调用上面的两个接口,但是现在只需要调用 create()
一个接口就行,可以少打几个字了 (^_^)/
## 图片示例
默认生成图片:
![image](media/example.png)
生成数学公式并且有颜色的验证码:
![image2](media/example-2.png)
## 为什么使用 svg 格式?
不需要引用 c++ 模块。
如果你认为可以用正则匹配text标签那就大错特错了。
这个项目使用了opentype.js把文字转化为了路径。
换句话说,你得到的是
'&lt;path fill="#444" d="M104.83 19.74L107.85 19.74L112 33.56L116.13 19.74L119.15 19.74L113.48 36.85...'
这样的路径没有text标签。所以SVG验证码可能比的图片普通验证码要更难识别因为你必须先做SVG到其它格式的转化。
## License
[MIT](LICENSE.md)

1
backend/node_modules/svg-captcha/code-of-conduct.md generated vendored Normal file
View File

@@ -0,0 +1 @@
[https://contributor-covenant.org/version/1/4/](https://contributor-covenant.org/version/1/4/)

BIN
backend/node_modules/svg-captcha/fonts/Comismsh.ttf generated vendored Normal file

Binary file not shown.

2
backend/node_modules/svg-captcha/fonts/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,2 @@
license: free
website: http://www.moorstation.org/typoasis/designers/gemnew/home.htm

118
backend/node_modules/svg-captcha/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,118 @@
/**
* config captcha generation options
*/
declare class ConfigObject {
/**
* default: true
* The length of the random string
*/
size?: number;
/**
* width of captcha
*/
width?: number;
/**
* height of captcha
*/
height?: number;
/**
* captcha text size
*/
fontSize?: number;
/**
* random character preset
*/
charPreset?: string;
/**
* default: false
* if false, captcha will be black and white
* otherwise, it will be randomly colorized
*/
color?: boolean;
/**
* default: false
* if set to true, it will draw with light grey color
* use if you have a site with dark theme
* only active when color is set to false
*/
inverse?: boolean;
/**
* default: ''
* filter out some characters
*/
ignoreChars?: string;
/**
* default: 1
* number of noise lines
*/
noise?: number;
/**
* default: white
* background color of svg image
*/
background?: string;
/**
* default: +
* the math operator to use, "+", "-" or "+/-"
* if unknown operator passed defaults to "+/-"
*/
mathOperator?: string;
/**
* default: 1
* min value of the math expression
*/
mathMin?: number;
/**
* default: 9
* max value of the math expression
*/
mathMax?: number;
}
/**
* result of captcha generation
*/
interface CaptchaObj {
/**
* the captcha text,
* store this in your session
*/
text: string,
/**
* the svg image in string,
* set type of image/svg before send to client side
*/
data: string
}
/**
* This method returns a object that has two props:
* data: svg image string
* text: captcha text
* @param {ConfigObject} [options]
* @return {CaptchaObj}
*/
export function create(options?: ConfigObject): CaptchaObj;
/**
* This method returns a object that has two props:
* data: svg image string
* text: captcha text
* note that this method generate a math expression
* this means that text is the result of the math expression
* @param {ConfigObject} [options]
* @return {CaptchaObj}
*/
export function createMathExpr(options?: ConfigObject): CaptchaObj;
/**
* Override the default font with your own
* @param {string} url
*/
export function loadFont(url: string): void;
/**
* captcha generation global setting
*/
export const options: ConfigObject;
/**
* return a random string
* @param {number} size
* @return {string}
*/
export function randomText(size: number): string;

1
backend/node_modules/svg-captcha/index.js generated vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = require('./lib');

46
backend/node_modules/svg-captcha/lib/ch-to-path.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
'use strict';
const assert = require('assert');
function rndPathCmd(cmd) {
const r = (Math.random() * 0.2) - 0.1;
switch (cmd.type) {
case 'M': case 'L':
cmd.x += r;
cmd.y += r;
break;
case 'Q': case 'C':
cmd.x += r;
cmd.y += r;
cmd.x1 += r;
cmd.y1 += r;
break;
default:
// Close path cmd
break;
}
return cmd;
}
module.exports = function (text, opts) {
const ch = text[0];
assert(ch, 'expect a string');
const fontSize = opts.fontSize;
const fontScale = fontSize / opts.font.unitsPerEm;
const glyph = opts.font.charToGlyph(ch);
const width = glyph.advanceWidth ? glyph.advanceWidth * fontScale : 0;
const left = opts.x - (width / 2);
const height = (opts.ascender + opts.descender) * fontScale;
const top = opts.y + (height / 2);
const path = glyph.getPath(left, top, fontSize);
// Randomize path commands
path.commands.forEach(rndPathCmd);
const pathData = path.toPathData();
return pathData;
};

3
backend/node_modules/svg-captcha/lib/char-preset.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

91
backend/node_modules/svg-captcha/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
'use strict';
const chToPath = require('./ch-to-path');
const random = require('./random');
const optionMngr = require('./option-manager');
const opts = optionMngr.options;
const getLineNoise = function (width, height, options) {
const hasColor = options.color;
const noiseLines = [];
const min = options.inverse ? 7 : 1;
const max = options.inverse ? 15 : 9;
let i = -1;
while (++i < options.noise) {
const start = `${random.int(1, 21)} ${random.int(1, height - 1)}`;
const end = `${random.int(width - 21, width - 1)} ${random.int(1, height - 1)}`;
const mid1 = `${random.int((width / 2) - 21, (width / 2) + 21)} ${random.int(1, height - 1)}`;
const mid2 = `${random.int((width / 2) - 21, (width / 2) + 21)} ${random.int(1, height - 1)}`;
const color = hasColor ? random.color() : random.greyColor(min, max);
noiseLines.push(`<path d="M${start} C${mid1},${mid2},${end}" stroke="${color}" fill="none"/>`);
}
return noiseLines;
};
const getText = function (text, width, height, options) {
const len = text.length;
const spacing = (width - 2) / (len + 1);
const min = options.inverse ? 10 : 0;
const max = options.inverse ? 14 : 4;
let i = -1;
const out = [];
while (++i < len) {
const x = spacing * (i + 1);
const y = height / 2;
const charPath = chToPath(text[i], Object.assign({x, y}, options));
const color = options.color ?
random.color(options.background) : random.greyColor(min, max);
out.push(`<path fill="${color}" d="${charPath}"/>`);
}
return out;
};
const createCaptcha = function (text, options) {
text = text || random.captchaText();
options = Object.assign({}, opts, options);
const width = options.width;
const height = options.height;
const bg = options.background;
if (bg) {
options.color = true;
}
const bgRect = bg ?
`<rect width="100%" height="100%" fill="${bg}"/>` : '';
const paths =
[].concat(getLineNoise(width, height, options))
.concat(getText(text, width, height, options))
.sort(() => Math.random() - 0.5)
.join('');
const start = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0,0,${width},${height}">`;
const xml = `${start}${bgRect}${paths}</svg>`;
return xml;
};
const create = function (options) {
const text = random.captchaText(options);
const data = createCaptcha(text, options);
return {text, data};
};
const createMathExpr = function (options) {
const expr = random.mathExpr(options.mathMin, options.mathMax, options.mathOperator);
const text = expr.text;
const data = createCaptcha(expr.equation, options);
return {text, data};
};
module.exports = createCaptcha;
module.exports.randomText = random.captchaText;
module.exports.create = create;
module.exports.createMathExpr = createMathExpr;
module.exports.options = opts;
module.exports.loadFont = optionMngr.loadFont;

32
backend/node_modules/svg-captcha/lib/option-manager.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
'use strict';
const path = require('path');
const opentype = require('opentype.js');
const charPreset = require('./char-preset');
const fontPath = path.join(__dirname, '../fonts/Comismsh.ttf');
const font = opentype.loadSync(fontPath);
const ascender = font.ascender;
const descender = font.descender;
const options = {
width: 150,
height: 50,
noise: 1,
color: false,
background: '',
size: 4,
ignoreChars: '',
fontSize: 56,
charPreset, font, ascender, descender
};
const loadFont = filepath => {
const font = opentype.loadSync(filepath);
options.font = font;
options.ascender = font.ascender;
options.descender = font.descender;
};
module.exports = {
options, loadFont
};

148
backend/node_modules/svg-captcha/lib/random.js generated vendored Normal file
View File

@@ -0,0 +1,148 @@
'use strict';
const opts = require('./option-manager').options;
const randomInt = function (min, max) {
return Math.round(min + (Math.random() * (max - min)));
};
const stripCharsFromString = function (string, chars) {
return string.split('').filter(char => chars.indexOf(char) === -1);
};
exports.int = randomInt;
exports.greyColor = function (min, max) {
min = min || 1;
max = max || 9;
const int = randomInt(min, max).toString(16);
return `#${int}${int}${int}`;
};
exports.captchaText = function (options) {
if (typeof options === 'number') {
options = {size: options};
}
options = options || {};
const size = options.size || 4;
const ignoreChars = options.ignoreChars || '';
let i = -1;
let out = '';
let chars = options.charPreset || opts.charPreset;
if (ignoreChars) {
chars = stripCharsFromString(chars, ignoreChars);
}
const len = chars.length - 1;
while (++i < size) {
out += chars[randomInt(0, len)];
}
return out;
};
const mathExprPlus = function(leftNumber, rightNumber){
const text = (leftNumber + rightNumber).toString();
const equation = leftNumber + '+' + rightNumber;
return {text, equation}
}
const mathExprMinus = function(leftNumber, rightNumber){
const text = (leftNumber - rightNumber).toString();
const equation = leftNumber + '-' + rightNumber;
return {text, equation}
}
/**
* Creates a simple math expression using either the + or - operator
* @param {number} [min] - The min value of the math expression defaults to 1
* @param {number} [max] - The max value of the math expression defaults to 9
* @param {string} [operator] - The operator(s) to use
* @returns {{equation: string, text: string}}
*/
exports.mathExpr = function (min, max, operator) {
min = min || 1;
max = max || 9;
operator = operator || '+';
const left = randomInt(min, max);
const right = randomInt(min, max);
switch(operator){
case '+':
return mathExprPlus(left, right)
case '-':
return mathExprMinus(left, right)
default:
return (randomInt(1, 2) % 2) ? mathExprPlus(left, right) : mathExprMinus(left, right);
}
};
// https://github.com/jquery/jquery-color/blob/master/jquery.color.js#L432
// The idea here is generate color in hsl first and convert that to rgb color
exports.color = function (bgColor) {
// Random 24 colors
// or based on step
const hue = randomInt(0, 24) / 24;
const saturation = randomInt(60, 80) / 100;
const bgLightness = bgColor ? getLightness(bgColor) : 1.0;
let minLightness;
let maxLightness;
if (bgLightness >= 0.5) {
minLightness = Math.round(bgLightness * 100) - 45;
maxLightness = Math.round(bgLightness * 100) - 25;
} else {
minLightness = Math.round(bgLightness * 100) + 25;
maxLightness = Math.round(bgLightness * 100) + 45;
}
const lightness = randomInt(minLightness, maxLightness) / 100;
const q = lightness < 0.5 ?
lightness * (lightness + saturation) :
lightness + saturation - (lightness * saturation);
const p = (2 * lightness) - q;
const r = Math.floor(hue2rgb(p, q, hue + (1 / 3)) * 255);
const g = Math.floor(hue2rgb(p, q, hue) * 255);
const b = Math.floor(hue2rgb(p, q, hue - (1 / 3)) * 255);
/* eslint-disable no-mixed-operators */
const c = ((b | g << 8 | r << 16) | 1 << 24).toString(16).slice(1);
return '#' + c;
};
function getLightness(rgbColor) {
if (rgbColor[0] !== '#') {
return 1.0; // Invalid color ?
}
rgbColor = rgbColor.slice(1);
if (rgbColor.length === 3) {
rgbColor = rgbColor[0] + rgbColor[0] +
rgbColor[1] + rgbColor[1] + rgbColor[2] + rgbColor[2];
}
const hexColor = parseInt(rgbColor, 16);
const r = hexColor >> 16;
const g = hexColor >> 8 & 255;
const b = hexColor & 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
return (max + min) / (2 * 255);
}
function hue2rgb(p, q, h) {
h = (h + 1) % 1;
if (h * 6 < 1) {
return p + (q - p) * h * 6;
}
if (h * 2 < 1) {
return q;
}
if (h * 3 < 2) {
return p + (q - p) * ((2 / 3) - h) * 6;
}
return p;
}

59
backend/node_modules/svg-captcha/package.json generated vendored Normal file
View File

@@ -0,0 +1,59 @@
{
"name": "svg-captcha",
"version": "1.4.0",
"description": "generate svg captcha in node.js or express.js",
"main": "index.js",
"scripts": {
"test": "jest",
"lint": "xo"
},
"repository": {
"type": "git",
"url": "git+https://github.com/steambap/svg-captcha.git"
},
"keywords": [
"captcha",
"svg",
"node captcha",
"captcha generator",
"captcha alternative"
],
"author": {
"name": "Weilin Shi",
"email": "934587911@qq.com"
},
"engines": {
"node": ">=4.x"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/steambap/svg-captcha/issues"
},
"homepage": "https://github.com/steambap/svg-captcha#readme",
"dependencies": {
"opentype.js": "^0.7.3"
},
"devDependencies": {
"jest": "^21.1.0",
"jest-environment-node-debug": "^2.0.0",
"xo": "^0.18.2"
},
"xo": {
"esnext": true,
"envs": [
"jest"
],
"rules": {
"linebreak-style": [
0
]
}
},
"typings": "index.d.ts",
"files": [
"fonts/*",
"lib/*",
"index.d.ts",
"*.md"
]
}