阿里云部署尝试
49
.fun/tmp/deploy/template.yml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
ROSTemplateFormatVersion: '2015-09-01'
|
||||||
|
Transform: 'Aliyun::Serverless-2018-04-03'
|
||||||
|
Resources:
|
||||||
|
llej-service:
|
||||||
|
Type: 'Aliyun::Serverless::Service'
|
||||||
|
Properties:
|
||||||
|
Role: >-
|
||||||
|
acs:ram::1196182666067174:role/aliyunfcgeneratedrole-cn-hangzhou-llej-service
|
||||||
|
LogConfig:
|
||||||
|
Project: llej-hd1
|
||||||
|
Logstore: llej-hd1
|
||||||
|
VpcConfig:
|
||||||
|
VpcId: vpc-bp1fu2excjgage7cjehom
|
||||||
|
VSwitchIds:
|
||||||
|
- vsw-bp1ko07xl7lwnjoftuobf
|
||||||
|
SecurityGroupId: sg-bp1aiw8pa6se0mo5iuw7
|
||||||
|
NasConfig:
|
||||||
|
UserId: 10003
|
||||||
|
GroupId: 10003
|
||||||
|
MountPoints:
|
||||||
|
- ServerAddr: '089ab49e4d-jty6.cn-hangzhou.nas.aliyuncs.com:/llej-service'
|
||||||
|
MountDir: /mnt/auto
|
||||||
|
InternetAccess: true
|
||||||
|
webFont:
|
||||||
|
Type: 'Aliyun::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
Handler: dist/src/index.handler
|
||||||
|
Runtime: nodejs10
|
||||||
|
Timeout: 60
|
||||||
|
MemorySize: 128
|
||||||
|
InstanceConcurrency: 1
|
||||||
|
EnvironmentVariables:
|
||||||
|
LD_LIBRARY_PATH: >-
|
||||||
|
/code/.fun/root/usr/local/lib:/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code/.fun/root/usr/lib64:/code/.fun/root/lib:/code/.fun/root/lib/x86_64-linux-gnu:/code/.fun/root/python/lib/python2.7/site-packages:/code/.fun/root/python/lib/python3.6/site-packages:/code:/code/lib:/usr/local/lib:/mnt/auto/root/usr/local/lib:/mnt/auto/root/usr/lib:/mnt/auto/root/usr/lib/x86_64-linux-gnu:/mnt/auto/root/usr/lib64:/mnt/auto/root/lib:/mnt/auto/root/lib/x86_64-linux-gnu:/mnt/auto/root/python/lib/python2.7/site-packages:/mnt/auto/root/python/lib/python3.6/site-packages
|
||||||
|
NODE_PATH: >-
|
||||||
|
/mnt/auto/node_modules:/usr/local/lib/node_modules:/code/node_modules
|
||||||
|
PATH: >-
|
||||||
|
/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code:/code/node_modules/.bin:/code/.fun/python/bin:/code/.fun/node_modules/.bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin:/mnt/auto/root/usr/local/bin:/mnt/auto/root/usr/local/sbin:/mnt/auto/root/usr/bin:/mnt/auto/root/usr/sbin:/mnt/auto/root/sbin:/mnt/auto/root/bin:/mnt/auto/python/bin:/mnt/auto/node_modules/.bin
|
||||||
|
PYTHONPATH: >-
|
||||||
|
/mnt/auto/python/lib/python2.7/site-packages:/mnt/auto/python/lib/python3.6/site-packages
|
||||||
|
PYTHONUSERBASE: /code/.fun/python
|
||||||
|
Events:
|
||||||
|
httpTrigger:
|
||||||
|
Type: HTTP
|
||||||
|
Properties:
|
||||||
|
AuthType: anonymous
|
||||||
|
Methods:
|
||||||
|
- POST
|
||||||
|
- GET
|
||||||
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/coverage
|
||||||
|
/.nyc_output
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
.cache
|
||||||
|
dist
|
||||||
|
asset/font
|
||||||
|
asset/dynamically
|
||||||
4
.nas.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
nasMappings:
|
||||||
|
llej-service:
|
||||||
|
- localNasDir: node_modules
|
||||||
|
remoteNasDir: /mnt/auto/node_modules
|
||||||
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
||||||
25
.template.yml.backup
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
ROSTemplateFormatVersion: '2015-09-01'
|
||||||
|
Transform: 'Aliyun::Serverless-2018-04-03'
|
||||||
|
Resources:
|
||||||
|
llej-service:
|
||||||
|
Type: 'Aliyun::Serverless::Service'
|
||||||
|
Properties:
|
||||||
|
Description: ''
|
||||||
|
NasConfig: Auto
|
||||||
|
webFont:
|
||||||
|
Type: 'Aliyun::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
Handler: dist/index.handler
|
||||||
|
Runtime: nodejs10
|
||||||
|
CodeUri: ./
|
||||||
|
Timeout: 60
|
||||||
|
EnvironmentVariables:
|
||||||
|
NODE_PATH: '/mnt/auto/node_modules:/usr/local/lib/node_modules'
|
||||||
|
Events:
|
||||||
|
httpTrigger:
|
||||||
|
Type: HTTP
|
||||||
|
Properties:
|
||||||
|
AuthType: ANONYMOUS
|
||||||
|
Methods:
|
||||||
|
- POST
|
||||||
|
- GET
|
||||||
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": false
|
||||||
|
}
|
||||||
103
README.md
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# web font 字体裁剪工具
|
||||||
|
|
||||||
|
## 起因
|
||||||
|
|
||||||
|
ui 需要展现一些特定的字体,但直接引入字体包又过大,于是想到了裁剪字体,一开始想的使用「字蛛」但他是针对静态网站的,而且实际他会多出许多英文的,估计是直接将源码中存在的文字都算进去了。
|
||||||
|
后来又找到阿里的「webfont」 但他的字体有限,项目又不开源,所以自己写了这个
|
||||||
|
|
||||||
|
## 尝试
|
||||||
|
|
||||||
|
[web font 在线站点](https://webfont.shenzilong.cn/)
|
||||||
|
|
||||||
|
请注意,由于这个服务器比较差,所以访问可能比较慢,且因为服务器空间问题我会不定时的清空生成的资源,所以请不要使用这个站点生成的在线资源,如有需要应当自行布设
|
||||||
|
|
||||||
|
## 目的与功能
|
||||||
|
|
||||||
|
1.裁剪字体包使其仅包含选中的字体
|
||||||
|
|
||||||
|
例如 如下图生成的字体包仅包含 「天地无极乾坤借法」
|
||||||
|

|
||||||
|
|
||||||
|
<video src="./doc_img/功能演示.mkv" controls="controls" width:100% height:auto></video>
|
||||||
|
|
||||||
|
其体积自然十分之小
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2.另外可以生成 css 直接复制可用,部署在公网便可永久访问
|
||||||
|
|
||||||
|
例如
|
||||||
|
|
||||||
|
```css
|
||||||
|
@font-face {
|
||||||
|
font-family: "QIJIC";
|
||||||
|
src: url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.eot"); /* IE9 */
|
||||||
|
src: url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||||
|
url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.woff") format("woff"), /* chrome, firefox */
|
||||||
|
url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.ttf") format("truetype"), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||||
|
url("http://127.0.0.1:3000/asset/font/1584680576469/令东齐伋复刻体.svg#QIJIC") format("svg"); /* iOS 4.1- */
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3.将 ttf 的字体包放置在 ./asset/font_src/ 目录下自然可以检测到新的可用字体,无需重启服务
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4.提供 zip 的整体下载方案
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 提供的服务
|
||||||
|
|
||||||
|
### 查询可用字体列表
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 生成压缩字体包
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
如图可见每个返回的字体资源,访问即可下载。另外在访问该目录下的 asset.zip 可以直接下载全部的文件,生成的资源目录结构见下图
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
注意,此接口是还支持 post 方式访问的,这样可以一次请求多个类型的字体文件,而且不会如同 get 方法那样有长度限制
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 动态生成字体
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 请注意
|
||||||
|
|
||||||
|
只支持生成 .ttf .eot .woff .svg 这几种格式
|
||||||
|
|
||||||
|
## 写项目时遇到的问题
|
||||||
|
|
||||||
|
1. 使用 svelte https://github.com/DeMoorJasper/parcel-plugin-svelte 通过这个插件使用 parcel 然后报 new 的错 需要限制 编译的版本,在package.json browserslist 字段限制一下版本就好
|
||||||
|
|
||||||
|
2. parcel 对 post purgecss 支持好像有问题,需要修改 postcss.config.js 文件他才能正确的删除样式
|
||||||
|
|
||||||
|
## 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i
|
||||||
|
npm run build
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
默认的访问地址是 http://127.0.0.1:3000
|
||||||
|
|
||||||
|
## 鸣谢
|
||||||
|
|
||||||
|
[字体天下](http://www.fonts.net.cn/commercial-free-32767/fonts-zh-1.html)
|
||||||
|
|
||||||
|
[fontmin](https://github.com/ecomfe/fontmin)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [崮生](https://shenzilong.cn/关于/mit.html)
|
||||||
BIN
asset/font_src/Alibaba-PuHuiTi-Heavy.ttf
Normal file
BIN
asset/font_src/令东齐伋复刻体.ttf
Normal file
BIN
asset/font_src/优设标题黑.ttf
Normal file
BIN
asset/font_src/问藏书房.ttf
Normal file
82
build/config.gypi
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Do not edit. File was generated by node-gyp's "configure" step
|
||||||
|
{
|
||||||
|
"target_defaults": {
|
||||||
|
"cflags": [],
|
||||||
|
"default_configuration": "Release",
|
||||||
|
"defines": [],
|
||||||
|
"include_dirs": [],
|
||||||
|
"libraries": [],
|
||||||
|
"msbuild_toolset": "v142",
|
||||||
|
"msvs_windows_target_platform_version": "10.0.18362.0"
|
||||||
|
},
|
||||||
|
"variables": {
|
||||||
|
"asan": 0,
|
||||||
|
"build_v8_with_gn": "false",
|
||||||
|
"coverage": "false",
|
||||||
|
"dcheck_always_on": 0,
|
||||||
|
"debug_nghttp2": "false",
|
||||||
|
"debug_node": "false",
|
||||||
|
"enable_lto": "false",
|
||||||
|
"enable_pgo_generate": "false",
|
||||||
|
"enable_pgo_use": "false",
|
||||||
|
"error_on_warn": "false",
|
||||||
|
"force_dynamic_crt": 0,
|
||||||
|
"host_arch": "x64",
|
||||||
|
"icu_data_in": "..\\..\\deps\\icu-tmp\\icudt67l.dat",
|
||||||
|
"icu_endianness": "l",
|
||||||
|
"icu_gyp_path": "tools/icu/icu-generic.gyp",
|
||||||
|
"icu_path": "deps/icu-small",
|
||||||
|
"icu_small": "false",
|
||||||
|
"icu_ver_major": "67",
|
||||||
|
"is_debug": 0,
|
||||||
|
"napi_build_version": "6",
|
||||||
|
"nasm_version": "2.14",
|
||||||
|
"node_byteorder": "little",
|
||||||
|
"node_debug_lib": "false",
|
||||||
|
"node_enable_d8": "false",
|
||||||
|
"node_install_npm": "true",
|
||||||
|
"node_module_version": 83,
|
||||||
|
"node_no_browser_globals": "false",
|
||||||
|
"node_prefix": "/usr/local",
|
||||||
|
"node_release_urlbase": "https://nodejs.org/download/release/",
|
||||||
|
"node_shared": "false",
|
||||||
|
"node_shared_brotli": "false",
|
||||||
|
"node_shared_cares": "false",
|
||||||
|
"node_shared_http_parser": "false",
|
||||||
|
"node_shared_libuv": "false",
|
||||||
|
"node_shared_nghttp2": "false",
|
||||||
|
"node_shared_openssl": "false",
|
||||||
|
"node_shared_zlib": "false",
|
||||||
|
"node_tag": "",
|
||||||
|
"node_target_type": "executable",
|
||||||
|
"node_use_bundled_v8": "true",
|
||||||
|
"node_use_dtrace": "false",
|
||||||
|
"node_use_etw": "true",
|
||||||
|
"node_use_node_code_cache": "true",
|
||||||
|
"node_use_node_snapshot": "true",
|
||||||
|
"node_use_openssl": "true",
|
||||||
|
"node_use_v8_platform": "true",
|
||||||
|
"node_with_ltcg": "true",
|
||||||
|
"node_without_node_options": "false",
|
||||||
|
"openssl_fips": "",
|
||||||
|
"openssl_is_fips": "false",
|
||||||
|
"shlib_suffix": "so.83",
|
||||||
|
"target_arch": "x64",
|
||||||
|
"v8_enable_31bit_smis_on_64bit_arch": 0,
|
||||||
|
"v8_enable_gdbjit": 0,
|
||||||
|
"v8_enable_i18n_support": 1,
|
||||||
|
"v8_enable_inspector": 1,
|
||||||
|
"v8_enable_lite_mode": 0,
|
||||||
|
"v8_enable_pointer_compression": 0,
|
||||||
|
"v8_no_strict_aliasing": 1,
|
||||||
|
"v8_optimized_debug": 1,
|
||||||
|
"v8_promise_internal_field_count": 1,
|
||||||
|
"v8_random_seed": 0,
|
||||||
|
"v8_trace_maps": 0,
|
||||||
|
"v8_use_siphash": 1,
|
||||||
|
"want_separate_host_toolset": 0,
|
||||||
|
"nodedir": "C:\\Users\\llej\\AppData\\Local\\node-gyp\\Cache\\14.5.0",
|
||||||
|
"standalone_static_library": 1,
|
||||||
|
"msbuild_path": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
doc_img/api/font_list.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
doc_img/api/fontmin.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
doc_img/api/fontmin_post.jpg
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
doc_img/api/generate_fonts_dynamically.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
doc_img/下载展示.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc_img/体积展示.jpg
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
doc_img/功能演示.mkv
Normal file
BIN
doc_img/生成的资源.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc_img/路径展示.jpg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
doc_img/页面截图.jpg
Normal file
|
After Width: | Height: | Size: 75 KiB |
4
nest-cli.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src"
|
||||||
|
}
|
||||||
15501
package-lock.json
generated
Normal file
78
package.json
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"name": "webfont",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"prebuild": "rimraf dist",
|
||||||
|
"build": "nest build && parcel build ./static/index.html",
|
||||||
|
"build:font": "nest build && parcel build ./static/index.html --public-url /font/",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
|
"start:web": "parcel ./static/index.html",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:dev": "nest start --watch",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/polyfill": "^7.8.7",
|
||||||
|
"@fullhuman/postcss-purgecss": "^2.1.0",
|
||||||
|
"@nestjs/common": "^6.7.2",
|
||||||
|
"@nestjs/core": "^6.7.2",
|
||||||
|
"@nestjs/platform-express": "^6.7.2",
|
||||||
|
"@nestjs/serve-static": "^2.1.0",
|
||||||
|
"@webserverless/fc-express": "^0.1.4",
|
||||||
|
"font-spider": "^1.3.5",
|
||||||
|
"fontmin": "^0.9.8",
|
||||||
|
"parcel-bundler": "^1.12.4",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"rimraf": "^3.0.0",
|
||||||
|
"rxjs": "^6.6.0",
|
||||||
|
"svelte": "^3.20.1",
|
||||||
|
"tailwindcss": "^1.2.0",
|
||||||
|
"zip-a-folder": "0.0.12",
|
||||||
|
"zip-folder": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^6.9.0",
|
||||||
|
"@nestjs/schematics": "^6.7.0",
|
||||||
|
"@nestjs/testing": "^6.7.1",
|
||||||
|
"@types/express": "^4.17.6",
|
||||||
|
"@types/jest": "^24.0.18",
|
||||||
|
"@types/node": "^12.7.5",
|
||||||
|
"@types/supertest": "^2.0.10",
|
||||||
|
"cssnano": "^4.1.10",
|
||||||
|
"jest": "^24.9.0",
|
||||||
|
"parcel-plugin-svelte": "^4.0.6",
|
||||||
|
"prettier": "^1.18.2",
|
||||||
|
"supertest": "^4.0.2",
|
||||||
|
"ts-jest": "^24.1.0",
|
||||||
|
"ts-loader": "^6.2.2",
|
||||||
|
"ts-node": "^8.10.2",
|
||||||
|
"tsconfig-paths": "^3.9.0",
|
||||||
|
"tslint": "^5.20.0",
|
||||||
|
"typescript": "^3.9.6"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".spec.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
},
|
||||||
|
"browserslist": "last 10 chrome versions"
|
||||||
|
}
|
||||||
23
postcss.config.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const tailwindcss = require('tailwindcss');
|
||||||
|
|
||||||
|
const purgecss = require('@fullhuman/postcss-purgecss')({
|
||||||
|
// Specify the paths to all of the template files in your project
|
||||||
|
content: [
|
||||||
|
'./static/**/*.svelte',
|
||||||
|
'./static/**/*.js',
|
||||||
|
'./static/**/*.ts',
|
||||||
|
'./static/*.svelte',
|
||||||
|
'./static/*.js',
|
||||||
|
'./static/*.ts',
|
||||||
|
`111 `
|
||||||
|
],
|
||||||
|
|
||||||
|
// Include any special characters you're using in this regular expression
|
||||||
|
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
|
||||||
|
});
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
tailwindcss,
|
||||||
|
...(process.env.NODE_ENV === 'development' ? [] : [purgecss]),
|
||||||
|
],
|
||||||
|
};
|
||||||
22
src/app.controller.spec.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
describe('AppController', () => {
|
||||||
|
let appController: AppController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const app: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
appController = app.get<AppController>(AppController);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('root', () => {
|
||||||
|
it('should return "Hello World!"', () => {
|
||||||
|
expect(appController.getHello()).toBe('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
85
src/app.controller.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Query,
|
||||||
|
Response,
|
||||||
|
Res,
|
||||||
|
Req,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { Stream } from 'stream';
|
||||||
|
import { req_par } from './req.decorator';
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
/** 压缩字体 */
|
||||||
|
@Get('fontmin')
|
||||||
|
font_min(@Query('text') text, @Query('font') font,@req_par('host_url') host_url:string) {
|
||||||
|
|
||||||
|
return this.appService.font_min(text, font,host_url);
|
||||||
|
}
|
||||||
|
@Post('fontmin')
|
||||||
|
font_min_post(@Body() body: { text: string; font: string }[],@req_par('host_url') host_url:string) {
|
||||||
|
const res = body.map(par => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.appService
|
||||||
|
.font_min(par.text, par.font,host_url)
|
||||||
|
.then(r => {
|
||||||
|
resolve({
|
||||||
|
font: par.font,
|
||||||
|
css:r,
|
||||||
|
status: 'success',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
resolve({
|
||||||
|
font: par.font,
|
||||||
|
status: 'failure',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return Promise.all(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回字体列表 */
|
||||||
|
@Get('font_list')
|
||||||
|
font_list() {
|
||||||
|
return this.appService.font_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 压缩字体 */
|
||||||
|
@Get('generate_fonts_dynamically*')
|
||||||
|
async generate_fonts_dynamically(
|
||||||
|
@Req() req: Request,
|
||||||
|
@Res() res,
|
||||||
|
@Query('text') text: string,
|
||||||
|
@Query('font') font: string,
|
||||||
|
@Query('temp') temp: string,
|
||||||
|
) {
|
||||||
|
const type = req.url.match(/\.(.*)\?/)[1];
|
||||||
|
res.set({
|
||||||
|
'Content-Type': `font/${type}`,
|
||||||
|
});
|
||||||
|
if (!text) return ' ';
|
||||||
|
const file = await this.appService.generate_fonts_dynamically(
|
||||||
|
text,
|
||||||
|
font,
|
||||||
|
temp,
|
||||||
|
type,
|
||||||
|
);
|
||||||
|
|
||||||
|
const bufferStream = new Stream.PassThrough();
|
||||||
|
bufferStream.end(file);
|
||||||
|
bufferStream.pipe(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function promise_execute_all<T>(params: Promise<T>[]) {}
|
||||||
21
src/app.module.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
|
import { join } from 'path';
|
||||||
|
console.log( join(__dirname, '..'));
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ServeStaticModule.forRoot({
|
||||||
|
rootPath: join(__dirname, '..'),
|
||||||
|
}),
|
||||||
|
ServeStaticModule.forRoot({
|
||||||
|
rootPath: join(__dirname, '../../asset'),
|
||||||
|
serveRoot: '/asset',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
142
src/app.service.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import Fontmin from 'fontmin';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
const { zip } = require('zip-a-folder');
|
||||||
|
|
||||||
|
const font_src = './asset/font_src/';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
font_list() {
|
||||||
|
const font_dir = join(__dirname, `../../${font_src}`);
|
||||||
|
return fs.readdir(font_dir);
|
||||||
|
}
|
||||||
|
async font_min(text: string, font: string, server_url: string) {
|
||||||
|
/** 因为 text 为 空或者是空格之类的 会导致 fontmin 运算很久 */
|
||||||
|
text += '●';
|
||||||
|
const srcPath = `${font_src}${font}.ttf`; // 字体源文件
|
||||||
|
|
||||||
|
const outPath =
|
||||||
|
(process.env.is_aliyun ? '/tmp/' : '') + `asset/font/${Date.now()}/`;
|
||||||
|
const destPath = (process.env.is_aliyun ? '/tmp/' : '.') + `/${outPath}`; // 输出路径
|
||||||
|
// 初始化
|
||||||
|
const fontmin = new Fontmin()
|
||||||
|
.src(srcPath) // 输入配置
|
||||||
|
.use(
|
||||||
|
Fontmin.glyph({
|
||||||
|
text, // 所需文字
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.use(Fontmin.ttf2eot()) // eot 转换插件
|
||||||
|
.use(Fontmin.ttf2woff()) // woff 转换插件
|
||||||
|
.use(Fontmin.ttf2svg()) // svg 转换插件
|
||||||
|
.use(Fontmin.css({ fontPath: `${server_url}${outPath}` })) // css 生成插件
|
||||||
|
.dest(destPath); // 输出配置
|
||||||
|
|
||||||
|
// 执行
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fontmin.run(function(err, files, stream) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
const css = files
|
||||||
|
.filter(f =>
|
||||||
|
(f.history[f.history.length - 1] as string).endsWith('.css'),
|
||||||
|
)
|
||||||
|
.map(f => f._contents.toString())[0];
|
||||||
|
zip(
|
||||||
|
join(__dirname, '../../', destPath),
|
||||||
|
join(__dirname, '../../', destPath, 'asset.zip'),
|
||||||
|
);
|
||||||
|
// resolve({code:0,fil:files.map(f=>f._contents.toString())}); // 成功
|
||||||
|
// resolve({code:0,files}); // 成功
|
||||||
|
resolve(css); // 成功
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async generate_fonts_dynamically(
|
||||||
|
text: string,
|
||||||
|
font: string,
|
||||||
|
temp: string,
|
||||||
|
type: string,
|
||||||
|
) {
|
||||||
|
text += '●';
|
||||||
|
const hash = crypto.createHash('md5');
|
||||||
|
hash.update(`${type}${font}${text}`);
|
||||||
|
const hash_str = hash.digest('hex');
|
||||||
|
const srcPath = `${font_src}${font}.ttf`; // 字体源文件
|
||||||
|
const outPath = `asset/dynamically/${hash_str}`;
|
||||||
|
const destPath = `./${outPath}`; // 输出路径
|
||||||
|
|
||||||
|
const full_path = join(__dirname, '../../', destPath, `${font}.${type}`);
|
||||||
|
/** 需要持久化 */
|
||||||
|
if (temp !== 'true') {
|
||||||
|
try {
|
||||||
|
return await fs.readFile(full_path);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`开始生成 ${full_path}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 初始化
|
||||||
|
const fontmin = new Fontmin()
|
||||||
|
.src(srcPath) // 输入配置
|
||||||
|
.use(
|
||||||
|
Fontmin.glyph({
|
||||||
|
text, // 所需文字
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ('eot' === type) {
|
||||||
|
fontmin.use(Fontmin.ttf2eot()); // eot 转换插件
|
||||||
|
}
|
||||||
|
if ('woff' === type) {
|
||||||
|
fontmin.use(Fontmin.ttf2woff()); // eot 转换插件
|
||||||
|
}
|
||||||
|
if ('svg' === type) {
|
||||||
|
fontmin.use(Fontmin.ttf2svg()); // eot 转换插件
|
||||||
|
}
|
||||||
|
/** 缓存数据 */
|
||||||
|
if (temp !== 'true') {
|
||||||
|
fontmin.dest(destPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fontmin.run(async function(err, files, stream) {
|
||||||
|
if (err) {
|
||||||
|
// 异常捕捉
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
const buffer = files.filter(f =>
|
||||||
|
/** 筛选需要的类型 */
|
||||||
|
(f.history[f.history.length - 1] as string).endsWith(type),
|
||||||
|
)[0]._contents;
|
||||||
|
resolve(buffer); // 成功
|
||||||
|
/** 存个日志 */
|
||||||
|
if (temp !== 'true') {
|
||||||
|
const content_path = join(
|
||||||
|
__dirname,
|
||||||
|
'../../',
|
||||||
|
destPath,
|
||||||
|
`content.txt`,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await fs.appendFile(
|
||||||
|
content_path,
|
||||||
|
`type:${type}\nfont:${font}\ntext:${text}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`写 ${content_path} 失败`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/config.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const config={
|
||||||
|
web_font_path:"//127.0.0.1:3000/"
|
||||||
|
}
|
||||||
24
src/index.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { ExpressAdapter } from '@nestjs/platform-express';
|
||||||
|
import { Server } from '@webserverless/fc-express';
|
||||||
|
import express from 'express';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
let p = (async () => {
|
||||||
|
const adapter = new ExpressAdapter(app);
|
||||||
|
const app2 = await NestFactory.create(AppModule, adapter);
|
||||||
|
app2.enableCors();
|
||||||
|
await app2.init();
|
||||||
|
})();
|
||||||
|
app.use(function(req, res, next) {
|
||||||
|
console.log(`▩▩▩[time:${Date.now()} [url:${req.url}]]`);
|
||||||
|
p.then(() => next());
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = new Server(app);
|
||||||
|
|
||||||
|
module.exports.handler = function(req, res, context) {
|
||||||
|
server.httpProxy(req, res, context);
|
||||||
|
};
|
||||||
15
src/main.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
import serveStatic from 'serve-static';
|
||||||
|
import { Response } from 'express';
|
||||||
|
import { logger } from './middleware/logger.middleware';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
app.enableCors();
|
||||||
|
app.use(logger);
|
||||||
|
|
||||||
|
await app.listen(3000);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
|
|
||||||
26
src/middleware/logger.middleware.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Response, Request } from 'express';
|
||||||
|
|
||||||
|
const NS_PER_SEC = 1e9;
|
||||||
|
export async function logger(req: Request, res: Response, next) {
|
||||||
|
const time = process.hrtime();
|
||||||
|
next();
|
||||||
|
res.once('finish', () => {
|
||||||
|
const diff = process.hrtime(time);
|
||||||
|
console.log(
|
||||||
|
`[${req.headers['x-forwarded-for'] ||
|
||||||
|
req.connection.remoteAddress ||
|
||||||
|
req.socket.remoteAddress ||
|
||||||
|
req.connection.remoteAddress}][${(diff[0] * NS_PER_SEC + diff[1]) /
|
||||||
|
1000000}]`,
|
||||||
|
decodeURI_catch(req.url),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeURI_catch(url: string) {
|
||||||
|
try {
|
||||||
|
return decodeURI(url);
|
||||||
|
} catch (error) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/req.decorator.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { createParamDecorator } from '@nestjs/common';
|
||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
export const req_par = createParamDecorator((data: string, req:Request) => {
|
||||||
|
if('host_url'===data){
|
||||||
|
return `//${req.headers.host}/`
|
||||||
|
}
|
||||||
|
return req
|
||||||
|
});
|
||||||
140
static/App.svelte
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<script>
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import { get_font, get_font_list,server,post_fontmin } from './req';
|
||||||
|
/** 可用的字体列表 {id:number,name:string:selected:undefined | boolen,css:undefined|string}*/
|
||||||
|
$: font_list = [];
|
||||||
|
get_font_list().then(r => {
|
||||||
|
font_list = r.map(ttf => ({ name: ttf.replace(/\.ttf$/, '') }));
|
||||||
|
});
|
||||||
|
/** 选择的文字 */
|
||||||
|
let text = '在此输入需要提取的文字\n在右侧选择字体\n然后点击下方的生成字体按钮';
|
||||||
|
/** 请求方式 */
|
||||||
|
let request_method="post"
|
||||||
|
/** 用于测试动态生成接口 */
|
||||||
|
let generate_fonts_dynamically=`<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: "test";
|
||||||
|
src:
|
||||||
|
url("${server}generate_fonts_dynamically.ttf?temp=true&font=优设标题黑&text=优设标题黑(直接改这里和前面的字体名看效果)") format("truetype");
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
</style>`
|
||||||
|
$: selected_font = font_list.filter(font => font.selected);
|
||||||
|
function generate_font() {
|
||||||
|
if('post'===request_method){
|
||||||
|
/** 使用 post 请求,单请求方式 */
|
||||||
|
post_fontmin(
|
||||||
|
selected_font.map(f=>({
|
||||||
|
font:f.name, text
|
||||||
|
}))
|
||||||
|
).then(res=>{
|
||||||
|
selected_font.forEach(font=>{
|
||||||
|
let r=res.find(o=>o.font===font.name).css
|
||||||
|
font_processing(font,r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if('get'===request_method){
|
||||||
|
/** 使用 get 请求,多请求方式 */
|
||||||
|
selected_font.forEach(font => {
|
||||||
|
get_font(font.name, text)
|
||||||
|
.then(r => {
|
||||||
|
font_processing(font,r)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function font_processing(font,r) {
|
||||||
|
r=r.replace(/\/\/.*?\//g,server)
|
||||||
|
|
||||||
|
const family = r.match(/font-family: "(.*)"/)[1];
|
||||||
|
font.css = r;
|
||||||
|
font.family = family;
|
||||||
|
font.zip=server+r.match(/(asset\/font\/\d+\/)/)[0]+'asset.zip'
|
||||||
|
/** 因为要触发其他更新则必须对这个变量重新赋值 */
|
||||||
|
font_list = font_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function copy(str) {
|
||||||
|
var input = document.getElementById("copy_box");
|
||||||
|
input.value=str
|
||||||
|
input.focus();
|
||||||
|
input.setSelectionRange(0, -1); // 全选
|
||||||
|
document.execCommand("copy")
|
||||||
|
alert(`复制成功\n${str}`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each font_list as font, i}
|
||||||
|
{@html "<style>"+font.css+'.'+font.name+"{font-family:"+font.family+"}</style>"}
|
||||||
|
{/each}
|
||||||
|
<h1 class="text-lg text-center mb-3 font-bold">web font 字体裁剪工具</h1>
|
||||||
|
<textarea id="copy_box" class="w-0 h-0 fixed -m-24" />
|
||||||
|
<div class="flex justify-evenly">
|
||||||
|
<textarea
|
||||||
|
bind:value={text}
|
||||||
|
class="border flex-1 m-1"
|
||||||
|
placeholder="在此输入需要提取的文字 在右侧选择字体 然后点击下方的生成字体按钮"
|
||||||
|
cols="40"
|
||||||
|
rows="3" />
|
||||||
|
<div class="flex-1 m-1 flex flex-wrap">
|
||||||
|
{#each font_list as font, i}
|
||||||
|
<div
|
||||||
|
on:click={e => (font.selected = !font.selected)}
|
||||||
|
class="c-label {font.selected ? 'c-label-selected' : ''}
|
||||||
|
{font.name}">
|
||||||
|
{font.name}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
<div on:click={generate_font} class="bg-red-200 text-red-600 rounded-md px-2 hover:bg-red-400 hover:text-white duration-75 flex items-center shadow-md">
|
||||||
|
生成字体
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex border ml-2 items-end">
|
||||||
|
<div
|
||||||
|
on:click={e => request_method="post"}
|
||||||
|
class="c-label {request_method==="post" ? 'c-label-selected' : ''}">
|
||||||
|
使用 post 请求
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
on:click={e => request_method="get"}
|
||||||
|
class="c-label {request_method==="get" ? 'c-label-selected' : ''}">
|
||||||
|
使用 get 请求
|
||||||
|
</div>
|
||||||
|
<div class="text-sm">* 具体区别请打开控制台查看请求</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#each selected_font as font, i}
|
||||||
|
<div class={font.name}>
|
||||||
|
<div style="font-size:2rem">{text}</div>
|
||||||
|
<div class="flex justify-end items-center text-xs">
|
||||||
|
{#if font.css}
|
||||||
|
<a class="text-blue-400 underline" href="/{font.zip}">下载压缩资源</a>
|
||||||
|
<div class="c-label mx-1 text-xs" on:click={copy(font.css)}>复制css</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div>{font.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
|
||||||
|
<h2 class="text-lg text-center my-3 font-bold"> 动态生成字体(generate_fonts_dynamically 接口)</h2>
|
||||||
|
<p class="ml-1">使用如下的方式引入,则可以直接使用</p>
|
||||||
|
<textarea
|
||||||
|
bind:value={generate_fonts_dynamically}
|
||||||
|
class="border flex-1 m-1 w-full text-lg"
|
||||||
|
placeholder="在此输入需要提取的文字"
|
||||||
|
rows="13"
|
||||||
|
style="font-family:test;" />
|
||||||
|
{@html generate_fonts_dynamically}
|
||||||
|
|
||||||
|
|
||||||
7
static/app.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.c-label {
|
||||||
|
@apply border m-1 rounded-md px-1 items-center h-6 text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-label-selected {
|
||||||
|
@apply bg-red-600 text-white;
|
||||||
|
}
|
||||||
3
static/index.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
19
static/index.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>web font</title>
|
||||||
|
<link rel="stylesheet" href="./index.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="p-4">
|
||||||
|
|
||||||
|
<div class="c-app"></div>
|
||||||
|
|
||||||
|
<!-- This script tag points to the source of the JS file we want to load and bundle -->
|
||||||
|
<script src="./index.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
6
static/index.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// import '@babel/polyfill';
|
||||||
|
import App from './App.svelte';
|
||||||
|
import "./app.css";
|
||||||
|
new App({
|
||||||
|
target: document.querySelector('.c-app'),
|
||||||
|
});
|
||||||
49
static/req.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
export const server = '//' + location.host + location.pathname;
|
||||||
|
/** get 方式压缩字体 */
|
||||||
|
export function get_font(font: string, text: string) {
|
||||||
|
return new Promise((rs, re) => {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.addEventListener('readystatechange', function() {
|
||||||
|
if (this.readyState === 4) {
|
||||||
|
rs(this.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
xhr.open(
|
||||||
|
'GET',
|
||||||
|
`${server}fontmin?font=${encodeURIComponent(
|
||||||
|
font,
|
||||||
|
)}&text=${encodeURIComponent(text)}`,
|
||||||
|
);
|
||||||
|
xhr.onerror = re;
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** post 方式压缩字体 */
|
||||||
|
export function post_fontmin(par:{font:string,text:string}[]) {
|
||||||
|
return new Promise((rs, re) => {
|
||||||
|
var data = JSON.stringify(par);
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.addEventListener('readystatechange', function() {
|
||||||
|
if (this.readyState === 4) {
|
||||||
|
rs(JSON.parse(this.responseText) );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
xhr.open('POST', `${server}fontmin`);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
xhr.onerror = re;
|
||||||
|
xhr.send(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function get_font_list(font: string, text: string) {
|
||||||
|
return new Promise((rs, re) => {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.addEventListener('readystatechange', function() {
|
||||||
|
if (this.readyState === 4) {
|
||||||
|
rs(JSON.parse(this.responseText));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
xhr.open('GET', `${server}font_list`);
|
||||||
|
xhr.onerror = re;
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
3
static/svelte.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
declare module '*.svelte' {
|
||||||
|
export default any;
|
||||||
|
}
|
||||||
6
tailwind.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
theme: {
|
||||||
|
},
|
||||||
|
variants: {},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
41
template.yml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
ROSTemplateFormatVersion: '2015-09-01'
|
||||||
|
Transform: 'Aliyun::Serverless-2018-04-03'
|
||||||
|
Resources:
|
||||||
|
llej-service:
|
||||||
|
Type: 'Aliyun::Serverless::Service'
|
||||||
|
Properties:
|
||||||
|
Role: >-
|
||||||
|
acs:ram::1196182666067174:role/aliyunfcgeneratedrole-cn-hangzhou-llej-service
|
||||||
|
LogConfig:
|
||||||
|
Project: llej-hd1
|
||||||
|
Logstore: llej-hd1
|
||||||
|
VpcConfig:
|
||||||
|
VpcId: vpc-bp1fu2excjgage7cjehom
|
||||||
|
VSwitchIds:
|
||||||
|
- vsw-bp1ko07xl7lwnjoftuobf
|
||||||
|
SecurityGroupId: sg-bp1aiw8pa6se0mo5iuw7
|
||||||
|
NasConfig:
|
||||||
|
UserId: 10003
|
||||||
|
GroupId: 10003
|
||||||
|
MountPoints:
|
||||||
|
- ServerAddr: '089ab49e4d-jty6.cn-hangzhou.nas.aliyuncs.com:/llej-service'
|
||||||
|
MountDir: /mnt/auto
|
||||||
|
InternetAccess: true
|
||||||
|
webFont:
|
||||||
|
Type: 'Aliyun::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
Handler: dist/src/index.handler
|
||||||
|
Runtime: nodejs10
|
||||||
|
CodeUri: ./
|
||||||
|
Timeout: 60
|
||||||
|
EnvironmentVariables:
|
||||||
|
NODE_PATH: '/mnt/auto/node_modules:/usr/local/lib/node_modules'
|
||||||
|
is_aliyun: 'true'
|
||||||
|
Events:
|
||||||
|
httpTrigger:
|
||||||
|
Type: HTTP
|
||||||
|
Properties:
|
||||||
|
AuthType: ANONYMOUS
|
||||||
|
Methods:
|
||||||
|
- POST
|
||||||
|
- GET
|
||||||
24
test/app.e2e-spec.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import * as request from 'supertest';
|
||||||
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
|
describe('AppController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [AppModule],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
app = moduleFixture.createNestApplication();
|
||||||
|
await app.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/ (GET)', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
9
test/jest-e2e.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
|
"rootDir": ".",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"testRegex": ".e2e-spec.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||||
|
}
|
||||||
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"baseUrl": "./",
|
||||||
|
"incremental": true
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
18
tslint.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"defaultSeverity": "error",
|
||||||
|
"extends": ["tslint:recommended"],
|
||||||
|
"jsRules": {
|
||||||
|
"no-unused-expression": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"quotemark": [true, "single"],
|
||||||
|
"member-access": [false],
|
||||||
|
"ordered-imports": [false],
|
||||||
|
"max-line-length": [true, 150],
|
||||||
|
"member-ordering": [false],
|
||||||
|
"interface-name": [false],
|
||||||
|
"arrow-parens": false,
|
||||||
|
"object-literal-sort-keys": false
|
||||||
|
},
|
||||||
|
"rulesDirectory": []
|
||||||
|
}
|
||||||