822 Commits

Author SHA1 Message Date
dependabot[bot]
38d52c4609 Bump js-yaml from 3.14.1 to 3.14.2 (#875)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 08:47:33 +08:00
dependabot[bot]
2b3f31a204 Bump cipher-base from 1.0.4 to 1.0.6 (#872)
Bumps [cipher-base](https://github.com/crypto-browserify/cipher-base) from 1.0.4 to 1.0.6.
- [Changelog](https://github.com/browserify/cipher-base/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/cipher-base/compare/v1.0.4...v1.0.6)

---
updated-dependencies:
- dependency-name: cipher-base
  dependency-version: 1.0.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 07:54:10 +08:00
dependabot[bot]
94332ee597 Bump sha.js from 2.4.11 to 2.4.12 (#873)
Bumps [sha.js](https://github.com/crypto-browserify/sha.js) from 2.4.11 to 2.4.12.
- [Changelog](https://github.com/browserify/sha.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/sha.js/compare/v2.4.11...v2.4.12)

---
updated-dependencies:
- dependency-name: sha.js
  dependency-version: 2.4.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 07:53:58 +08:00
dependabot[bot]
aa6dd044ba Bump form-data from 4.0.1 to 4.0.4 (#870)
Bumps [form-data](https://github.com/form-data/form-data) from 4.0.1 to 4.0.4.
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.1...v4.0.4)

---
updated-dependencies:
- dependency-name: form-data
  dependency-version: 4.0.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 08:25:56 +08:00
dependabot[bot]
aa9aa31c42 Bump on-headers and compression (#868)
Bumps [on-headers](https://github.com/jshttp/on-headers) and [compression](https://github.com/expressjs/compression). These dependencies needed to be updated together.

Updates `on-headers` from 1.0.2 to 1.1.0
- [Release notes](https://github.com/jshttp/on-headers/releases)
- [Changelog](https://github.com/jshttp/on-headers/blob/master/HISTORY.md)
- [Commits](https://github.com/jshttp/on-headers/compare/v1.0.2...v1.1.0)

Updates `compression` from 1.7.4 to 1.8.1
- [Release notes](https://github.com/expressjs/compression/releases)
- [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/compression/compare/1.7.4...v1.8.1)

---
updated-dependencies:
- dependency-name: on-headers
  dependency-version: 1.1.0
  dependency-type: indirect
- dependency-name: compression
  dependency-version: 1.8.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 11:09:24 +08:00
dependabot[bot]
bbdb1fb39d Bump brace-expansion from 1.1.11 to 1.1.12 (#866)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.11 to 1.1.12.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-02 10:00:46 +08:00
dependabot[bot]
b3f3594cd5 Bump pbkdf2 from 3.1.2 to 3.1.3 (#864)
Bumps [pbkdf2](https://github.com/crypto-browserify/pbkdf2) from 3.1.2 to 3.1.3.
- [Changelog](https://github.com/browserify/pbkdf2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/pbkdf2/compare/v3.1.2...v3.1.3)

---
updated-dependencies:
- dependency-name: pbkdf2
  dependency-version: 3.1.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-29 09:51:03 +08:00
dependabot[bot]
c0504fe62f Bump http-proxy-middleware from 2.0.4 to 2.0.9 (#862) 2025-05-29 17:25:24 +08:00
dependabot[bot]
0bc23c796d Bump axios from 0.28.0 to 1.8.2 (#859) 2025-05-29 17:25:04 +08:00
zhendery
011b291e05 fix(水印): 添加水印后编码图片时漏掉了保存质量参数 (#856)
Co-authored-by: zhendery <zhendery@qq.com>
2025-02-15 02:36:56 +08:00
zhendery
effe173d9c fix(svg): svg也需要“缩略图”,复制一份svg到本地缩略图目录,否则从远程服务器拉取可能会比较慢。 (#852) 2025-01-08 06:33:12 +08:00
zhendery
bdd440d38f fix(图片上传): (小概率)修复图片“格式转换”时不同的图使用同一个临时文件导致变成同一张图的问题 (#851) 2025-01-08 06:32:50 +08:00
zhendery
fe5e4d9a67 feat: 添加svg矢量图的支持 (#833) 2024-12-28 20:47:52 +08:00
熊孝兵
66d7a0dce1 Update README.md 2024-12-28 20:46:24 +08:00
dependabot[bot]
4f13d7d5a1 Bump ip and webpack-dev-server (#848)
Removes [ip](https://github.com/indutny/node-ip). It's no longer used after updating ancestor dependency [webpack-dev-server](https://github.com/webpack/webpack-dev-server). These dependencies need to be updated together.


Removes `ip`

Updates `webpack-dev-server` from 4.7.4 to 4.15.2
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/v4.15.2/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v4.7.4...v4.15.2)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
- dependency-name: webpack-dev-server
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:25:19 +08:00
dependabot[bot]
093ef71c2c Bump aws/aws-sdk-php from 3.261.13 to 3.336.6 (#849)
Bumps [aws/aws-sdk-php](https://github.com/aws/aws-sdk-php) from 3.261.13 to 3.336.6.
- [Release notes](https://github.com/aws/aws-sdk-php/releases)
- [Commits](https://github.com/aws/aws-sdk-php/compare/3.261.13...3.336.6)

---
updated-dependencies:
- dependency-name: aws/aws-sdk-php
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:24:54 +08:00
dependabot[bot]
4a87a90d5b Bump browserify-sign from 4.2.1 to 4.2.3 (#847)
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.3.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.3)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:24:29 +08:00
dependabot[bot]
17cb901046 Bump @babel/traverse from 7.17.3 to 7.26.4 (#846)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.17.3 to 7.26.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.4/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:24:16 +08:00
dependabot[bot]
c0ba47d47d Bump axios from 0.25.0 to 0.28.0 (#845)
Bumps [axios](https://github.com/axios/axios) from 0.25.0 to 0.28.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.28.0/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.25.0...v0.28.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:24:06 +08:00
dependabot[bot]
1d0cb15854 Bump follow-redirects from 1.14.9 to 1.15.9 (#844)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.9 to 1.15.9.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.9...v1.15.9)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:23:51 +08:00
dependabot[bot]
465ff63230 Bump ws from 8.5.0 to 8.18.0 (#843)
Bumps [ws](https://github.com/websockets/ws) from 8.5.0 to 8.18.0.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.5.0...8.18.0)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:23:37 +08:00
dependabot[bot]
04a421d3eb Bump webpack-dev-middleware from 5.3.1 to 5.3.4 (#842)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:23:26 +08:00
dependabot[bot]
2642719769 Bump webpack from 5.76.1 to 5.97.1 (#841)
Bumps [webpack](https://github.com/webpack/webpack) from 5.76.1 to 5.97.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.76.1...v5.97.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:23:14 +08:00
dependabot[bot]
4de97fd91e Bump phpseclib/phpseclib from 3.0.19 to 3.0.43 (#840)
Bumps [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib) from 3.0.19 to 3.0.43.
- [Release notes](https://github.com/phpseclib/phpseclib/releases)
- [Changelog](https://github.com/phpseclib/phpseclib/blob/master/CHANGELOG.md)
- [Commits](https://github.com/phpseclib/phpseclib/compare/3.0.19...3.0.43)

---
updated-dependencies:
- dependency-name: phpseclib/phpseclib
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:19:17 +08:00
dependabot[bot]
ee93913aac Bump path-to-regexp from 1.8.0 to 1.9.0 (#839)
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:18:59 +08:00
dependabot[bot]
e54c4e551c Bump league/commonmark from 2.3.9 to 2.6.0 (#834)
Bumps [league/commonmark](https://github.com/thephpleague/commonmark) from 2.3.9 to 2.6.0.
- [Release notes](https://github.com/thephpleague/commonmark/releases)
- [Changelog](https://github.com/thephpleague/commonmark/blob/2.6/CHANGELOG.md)
- [Commits](https://github.com/thephpleague/commonmark/compare/2.3.9...2.6.0)

---
updated-dependencies:
- dependency-name: league/commonmark
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:18:45 +08:00
dependabot[bot]
743409726f Bump send and express (#835)
Bumps [send](https://github.com/pillarjs/send) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `send` from 0.17.2 to 0.19.0
- [Release notes](https://github.com/pillarjs/send/releases)
- [Changelog](https://github.com/pillarjs/send/blob/master/HISTORY.md)
- [Commits](https://github.com/pillarjs/send/compare/0.17.2...0.19.0)

Updates `express` from 4.17.3 to 4.21.2
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.3...4.21.2)

---
updated-dependencies:
- dependency-name: send
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:18:07 +08:00
dependabot[bot]
2fa82cc231 Bump elliptic from 6.5.4 to 6.6.1 (#836)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.6.1.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.6.1)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:17:56 +08:00
dependabot[bot]
dd4c40d846 Bump cookie and express (#837)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `cookie` from 0.4.2 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.4.2...v0.7.1)

Updates `express` from 4.17.3 to 4.21.2
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.3...4.21.2)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:17:45 +08:00
dependabot[bot]
8acf78dac4 Bump laravel/framework from 9.52.4 to 9.52.17 (#838)
Bumps [laravel/framework](https://github.com/laravel/framework) from 9.52.4 to 9.52.17.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/11.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v9.52.4...v9.52.17)

---
updated-dependencies:
- dependency-name: laravel/framework
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:17:30 +08:00
dependabot[bot]
eb7c051512 Bump postcss from 8.4.12 to 8.4.31 (#723)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.12 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.12...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:14:05 +08:00
dependabot[bot]
5ec90a08a1 Bump semver from 5.7.1 to 5.7.2 (#694)
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-28 20:13:52 +08:00
dependabot[bot]
911275c13b Bump guzzlehttp/psr7 from 2.4.4 to 2.5.0 (#651)
Bumps [guzzlehttp/psr7](https://github.com/guzzle/psr7) from 2.4.4 to 2.5.0.
- [Release notes](https://github.com/guzzle/psr7/releases)
- [Changelog](https://github.com/guzzle/psr7/blob/2.5/CHANGELOG.md)
- [Commits](https://github.com/guzzle/psr7/compare/2.4.4...2.5.0)

---
updated-dependencies:
- dependency-name: guzzlehttp/psr7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-16 17:55:13 +08:00
dependabot[bot]
704a15896d Bump laminas/laminas-diactoros from 2.24.0 to 2.25.2 (#657)
Bumps [laminas/laminas-diactoros](https://github.com/laminas/laminas-diactoros) from 2.24.0 to 2.25.2.
- [Release notes](https://github.com/laminas/laminas-diactoros/releases)
- [Commits](https://github.com/laminas/laminas-diactoros/compare/2.24.0...2.25.2)

---
updated-dependencies:
- dependency-name: laminas/laminas-diactoros
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-16 17:55:03 +08:00
熊孝兵
c80b92b346 后台图片管理支持通过图片路径名称搜索。Fixed #640 2023-03-30 09:49:01 +08:00
熊孝兵
67fc980968 Merge remote-tracking branch 'origin/master' 2023-03-29 11:11:45 +08:00
熊孝兵
05af7dffb8 后台图片管理支持通过图片路径名称搜索。Fixed #640 2023-03-29 11:11:37 +08:00
dependabot[bot]
ee068513c6 Bump webpack from 5.70.0 to 5.76.1 (#633)
Bumps [webpack](https://github.com/webpack/webpack) from 5.70.0 to 5.76.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.70.0...v5.76.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-29 11:10:27 +08:00
熊孝兵
fbb022c877 改进 WebDav 以适配多种认证方式。Fixes #497, #520 2023-03-17 08:46:05 +08:00
dependabot[bot]
2b90d5eb75 Bump symfony/http-kernel from 6.0.11 to 6.0.20 (#608)
Bumps [symfony/http-kernel](https://github.com/symfony/http-kernel) from 6.0.11 to 6.0.20.
- [Release notes](https://github.com/symfony/http-kernel/releases)
- [Changelog](https://github.com/symfony/http-kernel/blob/6.2/CHANGELOG.md)
- [Commits](https://github.com/symfony/http-kernel/compare/v6.0.11...v6.0.20)

---
updated-dependencies:
- dependency-name: symfony/http-kernel
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-02 14:33:12 +08:00
dependabot[bot]
edb36f8a92 Bump json5 from 1.0.1 to 1.0.2 (#591)
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-18 14:26:33 +08:00
熊孝兵
a5bbb26ec8 Update README.md 2022-12-07 08:06:09 +08:00
dependabot[bot]
9394e590a8 Bump loader-utils from 1.4.1 to 1.4.2 (#564)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-18 09:26:51 +08:00
dependabot[bot]
0cb84a9662 Bump loader-utils from 1.4.0 to 1.4.1 (#561)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.1/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-09 10:45:22 +08:00
Wisp X
bb37a11bfb 降低缩略图质量 2022-10-21 10:25:47 +08:00
Jerry
d5230acdb3 修复前端图片水印路径提示错误问题 (#546)
* 修复前端水印上传目录提示错误的问题
e.g. 本应放在 /storage/app/public 目录下
     但前端提示放在 /public 目录下

* 修复编辑角色组中图片水印存储位置提示不正确bug
2022-10-14 09:10:48 +08:00
Wisp X
c66e58353a 修复图片无法重命名的 bug (fixed #536) 2022-10-09 17:46:51 +08:00
Wisp X
6661f35a15 图片处理质量默认值改为 75% 2022-10-09 10:18:52 +08:00
Jerry
5a8eb833be 修复前端水印上传目录提示错误的问题 (#540)
e.g. 本应放在 /storage/app/public 目录下
     但前端提示放在 /public 目录下
2022-09-28 08:41:10 +08:00
Wisp X
1925a1ae7a minio 策略增加 bucket_endpoint 选项配置 2022-09-20 09:01:49 +08:00
Frank Qing
bb9ef6e267 Support minio https url (#534) 2022-09-20 08:23:02 +08:00
Wisp X
923f567e0a 升级拓展 2022-08-17 13:36:19 +08:00
Wisp X
62809f5300 改进升级方式 2022-08-17 13:06:14 +08:00
Wisp X
c46207c278 支持使用角色组控制上传图片的质量于格式 (Closed #415) 2022-08-17 11:35:05 +08:00
Wisp X
289ecffbb1 上传时及时销毁 InterventionImage 实例以节省内存。 2022-08-17 10:48:54 +08:00
Wisp X
2fdb10a60f 改进阿里云 oss client 连接方式 2022-08-17 10:37:53 +08:00
Wisp X
b2b1693a2f 公告支持手动打开 (Closed #504) 2022-08-17 10:28:28 +08:00
Wisp X
b6f5f2405b 修复角色组设置水印模式,无法选择「动态生成」的 bug (Fixed #502) 2022-08-17 10:09:52 +08:00
Wisp X
f49f52dc34 修改接口文档文案 2022-08-17 10:04:21 +08:00
Wisp X
cd1436add2 update packages 2022-08-17 10:00:07 +08:00
Wisp X
aefbbda2bf Merge remote-tracking branch 'origin/master' 2022-08-05 17:49:49 +08:00
Wisp X
768ca9f24a 修复使用原文件名规则时返回了错误链接的 bug.(Fixed #496) 2022-08-05 17:35:10 +08:00
dependabot[bot]
fb9c390bc7 Bump terser from 4.8.0 to 4.8.1 (#488)
Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-21 08:10:56 +08:00
Wisp X
a4a60f13a0 修复文件名含有特殊字符导致排版异常的 bug.(Fixed #471) 2022-07-11 14:13:46 +08:00
TeacherDu
5071c3a904 fix a bug (#474)
* fix a bug

https://github.com/lsky-org/lsky-pro/issues/473

* Update index.blade.php

Co-authored-by: Wisp X <wisp-x@qq.com>
2022-07-11 08:06:10 +08:00
dependabot[bot]
2c52c87c95 Bump guzzlehttp/guzzle from 7.4.4 to 7.4.5 (#453)
Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.4.4 to 7.4.5.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/master/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.4.4...7.4.5)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-22 08:31:33 +08:00
Wisp X
3161394a83 改进 dropdown 组件 2022-06-15 10:32:15 +08:00
Wisp X
8f6da67f4f 改进 dropdown 组件 2022-06-15 10:30:53 +08:00
Wisp X
4d3bfed846 Merge remote-tracking branch 'origin/master' 2022-06-15 10:19:00 +08:00
Wisp X
2086daf1ae 改进公告弹窗 Closed #447 2022-06-15 10:17:55 +08:00
dependabot[bot]
a8ab0c6680 Bump guzzlehttp/guzzle from 7.4.3 to 7.4.4 (#445)
Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.4.3 to 7.4.4.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/master/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.4.3...7.4.4)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-10 14:53:04 +08:00
Wisp X
8b5bf74aa2 修复新用户注册后 IP 为空的 bug. 2022-06-02 08:10:24 +08:00
Wisp X
c0513f85a1 支持设置图片 url 额外参数 Closed #435 2022-05-30 11:14:32 +08:00
dependabot[bot]
8f0db089af Bump guzzlehttp/guzzle from 7.4.2 to 7.4.3 (#434)
Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.4.2 to 7.4.3.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/master/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.4.2...7.4.3)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-26 06:49:04 +08:00
Wisp X
d705542f44 fix a bug 2022-05-23 17:38:05 +08:00
Wisp X
4266fd60b7 水印支持切换为覆盖原图或动态生成 2022-05-23 17:28:45 +08:00
Wisp X
315b5b8296 改进样式 2022-05-20 11:28:42 +08:00
Wisp X
10699d69f6 fix a bug 2022-05-20 09:09:40 +08:00
Wisp X
58da33d3c9 取消索引创建迁移文件 2022-05-17 16:42:25 +08:00
Wisp X
20871efb5c 改进 2022-05-11 15:27:57 +08:00
Wisp X
912e3747ee fix a bug 2022-05-10 13:22:55 +08:00
Wisp X
fba0d77e7b fix a bug 2022-05-07 09:56:55 +08:00
Wisp X
10dea5cffd fix a bug 2022-05-06 09:30:54 +08:00
Wisp X
f7983a2f21 修复PC端开启平板模式后无法响应触摸事件的问题 Fixed #386 2022-04-29 11:16:50 +08:00
Wisp X
8ab428c593 改进样式 2022-04-29 09:47:17 +08:00
Wisp X
fb5f83f43c push version 2022-04-27 17:34:10 +08:00
Wisp X
73750890d2 build style 2022-04-27 17:32:27 +08:00
Wisp X
3ad7e711be build style 2022-04-27 17:01:50 +08:00
Wisp X
b6fd41841b 更新版本号 2022-04-27 16:59:33 +08:00
Wisp X
b61db2b31a fix a bug 2022-04-27 16:54:45 +08:00
Wisp X
cff42566c8 fix a bug 2022-04-27 16:30:44 +08:00
Wisp X
e44fabbd31 给图片表增加索引 2022-04-27 15:58:58 +08:00
Wisp X
3cdf495bef 给图片表增加索引 2022-04-27 15:53:52 +08:00
Wisp X
6291acbcee 给图片表增加索引 2022-04-27 15:51:53 +08:00
Wisp X
1434d6fa1c 改进 2022-04-27 15:34:41 +08:00
Wisp X
73c55b1a77 修复图片量太大导致内存溢出的 bug 2022-04-27 14:52:48 +08:00
TeacherDu
8780a21fe7 gravatar国内的节点 (#399) 2022-04-24 08:07:58 +08:00
Wisp X
3acabd7b38 fix a bug. 2022-04-21 14:14:34 +08:00
Wisp X
1f826fd652 默认信任所有代理 2022-04-19 17:10:11 +08:00
Wisp X
8d266b3d5a update packages. 2022-04-19 16:42:08 +08:00
Wisp X
02b9695882 修复时间不正确的 bug 2022-04-19 11:13:03 +08:00
Wisp X
5bba6d0d52 移除 alpinejs 控制台警告 2022-04-12 14:37:23 +08:00
Wisp X
52f9c0cbe8 update packages 2022-04-07 14:36:08 +08:00
Wisp X
d2b4910196 删除文件时同步删除缓存 2022-04-07 08:27:52 +08:00
Wisp X
9a5cbb2ea7 fix a bug 2022-04-06 08:45:54 +08:00
Wisp X
b9a3ea2c44 fix a bug 2022-04-06 08:19:51 +08:00
Wisp X
0d826042ad 更新版本号 2022-04-01 17:12:26 +08:00
Wisp X
f9a84c149c 增加一些 tips 2022-04-01 16:44:40 +08:00
Wisp X
798e72f634 适配路径支持设置为空 2022-04-01 16:20:23 +08:00
Wisp X
dec4e6fdb7 修复通过请求删除接口无法删除物理文件的 bug. Closed #366 2022-04-01 16:08:37 +08:00
Wisp X
3cecc20f44 改进首页底部在手机端的样式 2022-03-31 08:30:37 +08:00
Wisp X
86d67450ea 修复站点名称过长导致手机端侧边栏无法收起的 bug 2022-03-30 17:10:43 +08:00
Wisp X
aa9887715a 修复我的图片中添加/修改项目时回车出错的 bug. Closed 363 2022-03-30 16:26:11 +08:00
Wisp X
fc1c2095a6 up 2022-03-30 08:01:15 +08:00
Q-Y-H
a0d216fb2d 路径命名可为空 (#364) 2022-03-30 07:58:23 +08:00
Wisp X
b8471f2ca2 fix a bug 2022-03-25 08:48:02 +08:00
Wisp X
b7601272dd fix a bug 2022-03-25 08:41:06 +08:00
Wisp X
184dd51122 update README.md 2022-03-24 15:15:33 +08:00
Wisp X
3df02550c4 update README.md 2022-03-24 10:32:46 +08:00
Wisp X
f9b046a32f 图片审核跳过 ico 格式的图片 2022-03-24 10:25:34 +08:00
Wisp X
01c850739a update packages 2022-03-24 10:13:51 +08:00
Wisp X
43bb7792ac 增加 nsfwjs 图片审核驱动 Closed #347 2022-03-24 10:07:25 +08:00
Wisp X
6c31ace31a update packages 2022-03-24 07:55:51 +08:00
Wisp X
e9050f4b9b fix a bug 2022-03-23 17:40:06 +08:00
Wisp X
a2a45e72ba fix a bug 2022-03-23 17:31:03 +08:00
Wisp X
317c2ea369 Merge branch 'dev' 2022-03-23 17:28:14 +08:00
Wisp X
df2ed46ab2 fix 2022-03-23 17:27:51 +08:00
Wisp X
979b628f59 fix 2022-03-23 17:15:24 +08:00
Wisp X
71924b0397 支持腾讯云图片审核 2022-03-23 17:00:47 +08:00
Wisp X
b33c309a56 鉴黄跳过 psd、tif 格式的图片 2022-03-23 15:55:46 +08:00
Wisp X
ae785cc56f 支持设置粘贴图片后的动作 2022-03-23 15:45:36 +08:00
Wisp X
66423b1d18 上传时预览图片支持点击查看大图 2022-03-23 15:02:48 +08:00
Wisp X
ab21fb203f 改进升级方式 2022-03-23 13:09:15 +08:00
Wisp X
31503d340a 改进升级方式 2022-03-23 12:26:01 +08:00
Wisp X
165635bce9 改进升级方式 2022-03-23 12:25:42 +08:00
Wisp X
45a639fe3e Merge branch 'dev' 2022-03-22 17:35:32 +08:00
Wisp X
b22a334b86 format code 2022-03-22 17:35:26 +08:00
Wisp X
4be12d828d fix a bug 2022-03-22 17:34:53 +08:00
Wisp X
683a0a4888 修复翻页后搜索条件丢失的 bug Closed #343 2022-03-22 17:34:08 +08:00
Wisp X
769eb4bbd5 改进 2022-03-22 14:17:21 +08:00
Wisp X
10921b34fc update README.md 2022-03-22 14:10:47 +08:00
Wisp X
ede1ec5344 图片管理列表,psd、tiff 格式的图片预览时使用缩略图 #339 2022-03-22 08:13:41 +08:00
Wisp X
6f6403165c 图片管理列表,psd、tiff 格式的图片预览时使用缩略图 #339 2022-03-22 08:13:12 +08:00
Pumpkin
43b54a05ca 为底部ICP备案号链接添加target属性“_blank” (#340) 2022-03-22 06:27:33 +08:00
Wisp X
b1b5bd0782 增加在线更新超时时间 2022-03-21 14:24:05 +08:00
Wisp X
b2a4c5b971 增加在线更新超时时间 2022-03-21 14:23:19 +08:00
Wisp X
b86d5053f2 upgrade packages 2022-03-21 13:11:27 +08:00
Wisp X
aa4fd1c80a update version 2022-03-21 13:07:35 +08:00
Wisp X
ed48d6ca75 fix a bug 2022-03-21 11:35:47 +08:00
Wisp X
97a8c74e22 rollback 2022-03-21 10:02:53 +08:00
Wisp X
8d79647974 s3 支持设置 endpoint 2022-03-21 09:54:01 +08:00
Wisp X
07cd115048 增加安装要求 2022-03-21 09:40:20 +08:00
Wisp X
e227e5acd6 上传时设置文件以及文件夹可见性 2022-03-21 09:38:34 +08:00
Wisp X
2fb3eba233 上传时设置文件以及文件夹可见性 2022-03-21 09:36:02 +08:00
Wisp X
dde0e988f8 Merge branch 'master' into dev 2022-03-21 08:11:55 +08:00
梦彗業
32a9c2e85c 为底部ICP备案号添加工信部跳转 (#334) 2022-03-21 08:09:10 +08:00
Wisp X
5bd4e81ec5 在线更新后清除缓存 2022-03-18 15:40:20 +08:00
Wisp X
d428d535f5 删除本地策略时同时删除符号连接 2022-03-18 15:13:18 +08:00
Wisp X
3b1d566973 水印合成跳过 gif、ico 格式图片 2022-03-18 09:14:17 +08:00
Wisp X
659afb6a50 ico 格式图片在开启原图保护的情况下直接输出,不经过 InterventionImage 处理 2022-03-18 09:11:45 +08:00
Wisp X
59c490b5d3 修复 ico 以及部分带有动画的 webp 格式图片无法上传的 bug.
获取图片宽高使用 getimagesize 而非 imagick 以获取更好的兼容性和性能
2022-03-18 08:17:01 +08:00
Wisp X
f1af71d3f3 粘贴上传文件名改为时间戳 Closed #314 2022-03-17 09:14:30 +08:00
Wisp X
2511fd6abb 删除文件图片时同时删除缩略图 2022-03-16 21:11:02 +08:00
Wisp X
b2663b8ec0 修复我的图片页面删除图片后列表为空的bug 2022-03-16 17:55:59 +08:00
Wisp X
eee6346bec 修复管理后台无法冻结用户的bug Fixed #328 2022-03-16 11:13:59 +08:00
Wisp X
6edee6b783 update configs 2022-03-16 11:10:02 +08:00
Wisp X
e526d0b4c8 update composer.json 2022-03-16 10:10:03 +08:00
Wisp X
37c6ccd29d compatible swoole 2022-03-16 10:00:58 +08:00
Wisp X
b0c6a6defa add swoole options 2022-03-16 09:30:28 +08:00
Wisp X
3badd7f7a3 add extension laravel/octane 2022-03-16 09:01:56 +08:00
Wisp X
96ba030b03 修复细节错误 2022-03-15 08:58:42 +08:00
Wisp X
54d421f93b 修复文字水印字体颜色无法修改的bug 2022-03-15 08:55:44 +08:00
Wisp X
9495179f96 fix a bug 2022-03-15 08:45:12 +08:00
Wisp X
32f629bac9 改进 2022-03-15 08:42:11 +08:00
Wisp X
a9650b6780 Minio 增加区域配置 2022-03-15 08:03:55 +08:00
Wisp X
08e016e8b0 修复打开弹框时公告被同时打开的bug 2022-03-14 14:05:50 +08:00
Wisp X
bcffb4c8fc fix a bug 2022-03-14 11:44:54 +08:00
Wisp X
985af20b5b remove thumbnail route 2022-03-14 11:38:08 +08:00
Wisp X
0c8e4f20c3 角色组增加 tips 2022-03-14 10:47:04 +08:00
Wisp X
86cc72f27a fix a bug 2022-03-14 10:40:34 +08:00
Wisp X
bf26cb6699 增加生成缩略图命令 2022-03-14 10:35:41 +08:00
Wisp X
928727c149 修复关闭游客上传后公告无法弹出的问题 2022-03-14 10:17:15 +08:00
Wisp X
db9d878fe2 更改缩略图生成逻辑 2022-03-14 10:00:07 +08:00
Wisp X
8663fb4134 修复图片不属于任何策略时无法查看详情的bug 2022-03-14 08:12:30 +08:00
Wisp X
7464decb8e fix a bug 2022-03-13 20:02:26 +08:00
Wisp X
7d5df4cfba remove polyfilljs 2022-03-13 19:58:54 +08:00
Wisp X
03f0544ff4 修复删除策略或删除组导致500错误的bug 2022-03-13 18:34:11 +08:00
Wisp X
ca5a215fc9 修复 psd、tif 格式图片无法上传的bug 2022-03-13 17:29:13 +08:00
Wisp X
4ba44d7a16 add polyfill 2022-03-13 16:08:32 +08:00
Wisp X
476d797191 修复首次进入首页上传时提示未知策略的bug 2022-03-13 15:50:00 +08:00
Wisp X
1714e7c876 修复图片宽高过小导致我的图片页面展示异常的bug 2022-03-13 15:08:53 +08:00
Wisp X
a3acf85199 修复管理员修改用户密码将明文入库的bug 2022-03-13 15:05:37 +08:00
Wisp X
6d0631b494 upgrade packages 2022-03-13 13:23:17 +08:00
Wisp X
d465172509 修复备案号不显示的 bug 2022-03-13 13:20:32 +08:00
Wisp X
f2089c96ca fix a bug 2022-03-13 13:17:36 +08:00
Wisp X
e9e2a1eb0e 修复无法上传大写拓展名的文件 2022-03-13 13:11:15 +08:00
Wisp X
51b2682dd0 登录页面增加注册超链接 2022-03-13 12:24:58 +08:00
Wisp X
d0bd143e17 修复后台图片管理中图片大小显示错误的bug 2022-03-13 11:52:02 +08:00
Wisp X
aab42fb630 忽略默认符号连接 2022-03-13 09:54:40 +08:00
Wisp X
2389f7b39b 修复相册图片不能加载更多的bug 2022-03-13 09:47:12 +08:00
Wisp X
503b7d8249 fix a bug 2022-03-13 03:19:00 +08:00
Wisp X
87f7c500c1 fix a bug 2022-03-13 03:15:39 +08:00
Wisp X
69209f75d3 修复创建相册后用户相册数量未改变的bug 2022-03-13 02:17:23 +08:00
Wisp X
8b2bae00d8 修复我的图片删除后选中菜单未消失的bug 2022-03-13 02:08:43 +08:00
Wisp X
cf8516d889 图片上传服务取消数据库事务使用,避免并发上传时出现死锁。 2022-03-13 02:02:03 +08:00
Wisp X
435abde829 修复上传 jpeg 格式的图片被重命名为 jpg 的 bug 2022-03-12 23:58:05 +08:00
Wisp X
7d5cd6c825 fix a bug 2022-03-12 23:36:24 +08:00
Wisp X
47db995a54 Merge branch 'master' into dev 2022-03-12 21:30:02 +08:00
Wisp X
de11905441 hello lsky-pro 2.0 2022-03-12 20:52:19 +08:00
Wisp X
4ee82c37b0 hello lsky-pro 2.0 Closed #281 2022-03-12 20:49:15 +08:00
Wisp X
4bf87a11c9 增加安装条件 2022-03-12 19:16:21 +08:00
Wisp X
bf668edcae update package 2022-03-12 17:57:21 +08:00
Wisp X
ac62a5613a 关闭游客上传直接重定向至登录页面 2022-03-12 17:40:03 +08:00
Wisp X
abc0f70503 动态改变进度条颜色 2022-03-12 17:30:41 +08:00
Wisp X
5781077cfa 修复缩略图中带有大写的拓展名导致无法输出图片的bug 2022-03-12 16:31:00 +08:00
Wisp X
90cb1ec918 update README.md 2022-03-12 14:19:00 +08:00
Wisp X
485b326c96 原图保护增加清除缓存功能 2022-03-12 11:12:10 +08:00
Wisp X
45021411ae 增加安装条件 2022-03-12 09:49:17 +08:00
Wisp X
58c34a2548 upgrade package 2022-03-11 23:40:37 +08:00
Wisp X
39f1bb34f0 表格增加滚动条 2022-03-11 23:12:00 +08:00
Wisp X
5c4c1b6a65 修复选择多个文件时某个文件未通过校验导致添加到队列失败的 bug 2022-03-11 22:43:14 +08:00
Wisp X
5d29e7e0d1 fix a bug 2022-03-11 22:02:32 +08:00
Wisp X
89652c0ae3 add license 2022-03-10 17:29:35 +08:00
Wisp X
a96e5e099c upgrade package 2022-03-10 13:18:49 +08:00
Wisp X
577b904aa0 fix a bug 2022-03-10 12:22:32 +08:00
Wisp X
0b5abe9b05 update package 2022-03-10 11:34:44 +08:00
Wisp X
1053fccc9b 适配又拍云储存策略 2022-03-10 10:15:06 +08:00
Wisp X
4795c74724 fix a bugs 2022-03-09 08:19:28 +08:00
Wisp X
8f611098f7 update README.md 2022-03-08 21:13:50 +08:00
Wisp X
39ad13cbad update packages 2022-03-08 21:04:01 +08:00
Wisp X
eda5d2dd90 update packages 2022-03-08 20:50:23 +08:00
Wisp X
f9d67996e5 更新 README.md 2022-03-08 13:14:19 +08:00
Wisp X
34541421ed 更新 README.md 2022-03-08 13:07:49 +08:00
Wisp X
a9eacc24d2 更新 README.md 2022-03-08 13:01:17 +08:00
Wisp X
eae7321de3 更新 README.md 2022-03-08 12:36:21 +08:00
Wisp X
16b9ea1dbb 更新 README.md 2022-03-08 11:35:02 +08:00
Wisp X
c623535109 🐛 Fixing a bug. 2022-03-07 11:19:12 +08:00
Wisp X
9996af93f8 🐛 Fixing a bug. 2022-03-07 11:09:22 +08:00
Wisp X
43f8900cdd 🎨 改进结构 2022-03-07 08:19:43 +08:00
Wisp X
cebf64a4bb 🐛 修复 BUG 2022-03-06 21:32:41 +08:00
Wisp X
4ea2f28fd4 配置 workflows 2022-03-06 21:01:26 +08:00
Wisp X
a63527c086 配置 workflows 2022-03-06 20:37:21 +08:00
Wisp X
f725dce3a9 🐛 修复 BUG 2022-03-06 20:22:58 +08:00
Wisp X
134ceefe19 配置 workflows 2022-03-06 20:01:35 +08:00
Wisp X
7cd5a84f33 配置 workflows 2022-03-06 19:59:49 +08:00
Wisp X
af46c54b15 配置 workflows 2022-03-06 19:23:59 +08:00
Wisp X
ef08cf6fa8 配置 workflows 2022-03-06 18:02:44 +08:00
Wisp X
2bd6f6c23d 配置 workflows 2022-03-06 17:58:09 +08:00
Wisp X
f9f5d121c2 🐛 修复 BUG 2022-03-06 17:25:06 +08:00
Wisp X
338ade3090 🐛 修复 BUG 2022-03-06 17:22:19 +08:00
Wisp X
65e14f78f3 🐛 修复 BUG 2022-03-06 15:36:34 +08:00
Wisp X
f7982d89e8 增加命名规则对照表 2022-03-06 15:24:32 +08:00
Wisp X
e273b30e08 🐛 修复 BUG 2022-03-06 14:17:02 +08:00
Wisp X
677a0afeee 增加策略列表接口,接口支持上传时指定策略 2022-03-06 14:11:30 +08:00
Wisp X
0d1fd3461a 🐛 修复 BUG 2022-03-06 13:47:25 +08:00
Wisp X
551ee7014c 支持上传时动态切换储存策略 2022-03-06 13:44:11 +08:00
Wisp X
b74049bb37 🐛 修复 BUG 2022-03-06 12:15:55 +08:00
Wisp X
67ff633a3f 增加自定义js的支持 2022-03-06 11:30:05 +08:00
Wisp X
b69bc02b27 改进安装程序 2022-03-05 23:53:31 +08:00
Wisp X
0a8bfe6bf1 改进安装程序 2022-03-05 23:41:47 +08:00
Wisp X
a2daf5a404 🐛 修复 BUG 2022-03-05 23:26:02 +08:00
Wisp X
b582dd9b3e 增加安装异常的日志 2022-03-05 23:05:37 +08:00
Wisp X
c30e1a96aa 增加安装异常的日志 2022-03-05 22:46:30 +08:00
Wisp X
162900a9c5 🐛 修复 BUG 2022-03-05 21:41:52 +08:00
Wisp X
e1036e8fe7 🐛 修复 BUG 2022-03-05 21:39:25 +08:00
Wisp X
23678302ba 🐛 修复 BUG 2022-03-05 21:34:47 +08:00
Wisp X
9c7920049f 增加 minio 策略的支持 2022-03-05 20:51:11 +08:00
Wisp X
072f4bff70 完成 s3 储存策略 2022-03-05 19:59:42 +08:00
Wisp X
b7daf09bed 📦 Updating compiled files or packages. 2022-03-05 15:42:02 +08:00
Wisp X
6f80343706 完成阿里云oss储存策略 2022-03-05 15:31:32 +08:00
Wisp X
7f83aa828a 完成 webdav 储存方式 2022-03-05 15:00:07 +08:00
Wisp X
aed749a86d 完成 ftp 储存方式 2022-03-05 13:08:53 +08:00
Wisp X
68a1a8664a 完成sftp储存策略 2022-03-05 12:10:10 +08:00
Wisp X
2a7dd080ef 完善腾讯云储存策略 2022-03-05 10:01:58 +08:00
Wisp X
df52d48a31 完成腾讯云cos储存策略 2022-03-05 09:50:44 +08:00
Wisp X
4620003d9a 🐛 修复 BUG 2022-03-05 01:52:55 +08:00
Wisp X
a43df045d7 🐛 修复 BUG 2022-03-05 01:51:00 +08:00
Wisp X
43b5b09714 支持修改用户的角色组 2022-03-05 01:47:31 +08:00
Wisp X
7c4865ade9 七牛云储存策略 2022-03-05 01:39:28 +08:00
Wisp X
3f05eff482 更新 README.md 2022-03-04 18:30:53 +08:00
Wisp X
414a187bea 📦 Updating compiled files or packages. 2022-03-04 18:21:19 +08:00
Wisp X
d987ebda63 更新 ico 图标 2022-03-04 17:13:00 +08:00
Wisp X
ca4084fc35 更新 README.md 2022-03-04 16:59:39 +08:00
Wisp X
fe3ed2eefd 更新 logo 2022-03-04 16:26:56 +08:00
Wisp X
8f037bdde0 增加用户uid命名规则 2022-03-04 16:06:57 +08:00
Wisp X
46c1f7b911 改进升级方式 2022-03-04 15:22:04 +08:00
Wisp X
d47109fd5c 完成更新程序 2022-03-04 14:25:56 +08:00
Wisp X
d19eb061b1 🐛 修复 BUG 2022-03-03 11:35:37 +08:00
Wisp X
2bccbb72d8 💄 Updating the UI and style files. 2022-03-03 10:38:54 +08:00
Wisp X
a903680487 🐛 修复 BUG 2022-03-01 12:36:14 +08:00
Wisp X
ccddb8a0fd 🐛 修复 BUG 2022-03-01 12:33:55 +08:00
Wisp X
234defc719 🐛 修复 BUG 2022-03-01 12:19:28 +08:00
Wisp X
2e09013bb7 改进 2022-03-01 11:51:23 +08:00
Wisp X
843abacdef 适配游客组能使用多个储存的特性 2022-03-01 11:39:51 +08:00
Wisp X
60a7d0a87d 💄 Updating the UI and style files. 2022-02-28 17:58:02 +08:00
Wisp X
86edc377d1 改进升级方式 2022-02-28 17:05:39 +08:00
Wisp X
dada4805c4 改进升级方式 2022-02-28 17:03:05 +08:00
Wisp X
694669187f Introducing new features. 2022-02-28 15:14:26 +08:00
Wisp X
9bf925cced 增加安装要求 2022-02-28 09:53:25 +08:00
Wisp X
cfc898bda8 改进升级页面样式 2022-02-28 09:14:15 +08:00
Wisp X
ebb352782f 系统更新页面布局 2022-02-26 17:46:25 +08:00
Wisp X
c108e9bc76 修复移动图片,相册数量更新错误的 bug. 2022-02-25 17:07:19 +08:00
Wisp X
4e59097013 增加首页公告功能 2022-02-25 15:53:22 +08:00
Wisp X
82a98a3374 💄 Updating the UI and style files. 2022-02-25 15:02:21 +08:00
Wisp X
9a178dd3de 🐛 修复 BUG 2022-02-25 14:56:06 +08:00
Wisp X
c8e84dcb62 💄 Updating the UI and style files. 2022-02-25 14:51:56 +08:00
Wisp X
5cc8320f14 🐛 修复 BUG 2022-02-25 14:49:01 +08:00
Wisp X
0bdad8b4fe 适配多数据库 2022-02-25 13:52:30 +08:00
Wisp X
b899a3bf08 适配多数据库 2022-02-25 11:09:35 +08:00
Wisp X
697aa5bb66 适配多数据库 2022-02-25 11:05:21 +08:00
Wisp X
27ccdb4c73 改进模型 2022-02-25 09:25:44 +08:00
Wisp X
c1de4d03d0 Introducing new features. 2022-02-25 09:04:05 +08:00
Wisp X
af0d3db299 🐛 修复 BUG 2022-02-25 08:57:47 +08:00
Wisp X
f3c50617b5 增加应用 url 配置 2022-02-25 08:50:14 +08:00
Wisp X
4b1779bf38 💄 显示违规图片标记 2022-02-25 08:19:28 +08:00
Wisp X
590d2b7361 完善图片审核功能 2022-02-24 18:48:30 +08:00
Wisp X
0c31926fd4 完善图片审核功能 2022-02-24 18:47:22 +08:00
Wisp X
b753f5fa0e 改进角色组编辑 2022-02-24 17:38:21 +08:00
Wisp X
8b23b16b5c 🐛 修复 BUG 2022-02-24 17:27:16 +08:00
Wisp X
ef45e36d4d 改进安装页面 2022-02-24 16:15:24 +08:00
Wisp X
6b04113f61 改进安装页面 2022-02-24 16:13:18 +08:00
Wisp X
6135e29936 改进图片管理 2022-02-24 15:58:48 +08:00
Wisp X
af9d787efe 完成安装程序。 2022-02-24 15:41:01 +08:00
Wisp X
6172fc0a8c 改进安装 2022-02-24 09:03:45 +08:00
Wisp X
ddca7a1a22 🔧 发布异常页面配置 2022-02-22 17:23:14 +08:00
Wisp X
aa122a394d 画廊页面 2022-02-22 15:31:23 +08:00
Wisp X
09600cf561 改进检测安装逻辑 2022-02-22 15:26:26 +08:00
Wisp X
53ae724620 完善安装命令 2022-02-22 08:48:37 +08:00
Wisp X
2d905a5550 数据库安装指令 2022-02-22 08:31:55 +08:00
Wisp X
144f2650a2 📦 Updating compiled files or packages. 2022-02-21 14:28:30 +08:00
Wisp X
4b9213f358 🐛 修复 BUG 2022-02-21 08:10:56 +08:00
Wisp X
7ad296b74b 完善接口文档 2022-02-20 17:29:24 +08:00
Wisp X
7f53c90128 个人信息接口 2022-02-20 17:13:27 +08:00
Wisp X
6f08b30986 删除相册接口 2022-02-20 16:50:36 +08:00
Wisp X
0f26dfd8a0 相册列表接口 2022-02-20 16:45:32 +08:00
Wisp X
cb0cce0c09 图片删除接口 2022-02-20 16:30:14 +08:00
Wisp X
b507d71ddd 图片列表接口 2022-02-20 16:13:42 +08:00
Wisp X
c047b0a27e 清空 token 接口 2022-02-20 15:17:07 +08:00
Wisp X
c18d2d3169 token 生成接口 2022-02-20 15:08:05 +08:00
Wisp X
b6429aa7cf 接口请求限流 2022-02-20 14:27:23 +08:00
Wisp X
4342c47189 🐛 修复 BUG 2022-02-20 14:10:39 +08:00
Wisp X
33884c3fd1 上传图片接口 2022-02-20 12:54:39 +08:00
Wisp X
b9b3bde750 Introducing new features. 2022-02-20 10:46:37 +08:00
Wisp X
42e3ab4865 📦 Updating compiled files or packages. 2022-02-18 18:51:11 +08:00
Wisp X
e15026749c 完成图片管理功能 2022-02-18 18:49:41 +08:00
Wisp X
1510389709 改进用户管理 2022-02-18 13:58:27 +08:00
Wisp X
e27e539aa3 改进 & 修复 bug 2022-02-18 13:49:31 +08:00
Wisp X
5bbf2ef8d4 图片管理 2022-02-18 13:38:50 +08:00
Wisp X
da0da0ab09 🐛 修复 BUG 2022-02-18 12:09:08 +08:00
Wisp X
36397497a3 管理图片删除 2022-02-18 11:47:54 +08:00
Wisp X
28dc5b0e2e 💄 Updating the UI and style files. 2022-02-17 16:45:58 +08:00
Wisp X
c320c556bc 💄 Updating the UI and style files. 2022-02-17 16:25:42 +08:00
Wisp X
7bb19b6329 改进 2022-02-17 11:49:47 +08:00
Wisp X
0b75785b5e 🐛 修复 BUG 2022-02-17 11:11:07 +08:00
Wisp X
93f7e111ba 🐛 修复 BUG 2022-02-16 15:52:00 +08:00
Wisp X
4c95073963 📦 Updating compiled files or packages. 2022-02-16 15:11:32 +08:00
Wisp X
8c80e70696 增加测试邮件 2022-02-14 14:35:04 +08:00
Wisp X
0e996b579d 完善配置 2022-02-14 14:04:19 +08:00
Wisp X
11857bbe9d 🔧 Changing configuration files. 2022-02-14 11:36:19 +08:00
Wisp X
39238a61d7 🐛 修复 BUG 2022-02-13 17:50:34 +08:00
Wisp X
97d186c5d6 系统设置功能 2022-02-13 17:40:59 +08:00
Wisp X
6305d5b08c 系统设置页 2022-02-13 13:04:07 +08:00
Wisp X
cada455745 仪表盘 2022-02-13 01:13:27 +08:00
Wisp X
90239f9827 🐛 修复 BUG 2022-02-12 22:07:19 +08:00
Wisp X
a7c0620b3e 画廊页面 2022-02-12 19:53:24 +08:00
Wisp X
07e63ccba6 🐛 Fixing a bug. 2022-02-12 19:50:28 +08:00
Wisp X
09d5945e2e 画廊页面 2022-02-11 16:07:08 +08:00
Wisp X
c9fad14bf7 改进画廊样式 2022-02-11 16:05:57 +08:00
Wisp X
57f51e4e43 画廊功能 2022-02-11 15:50:48 +08:00
Wisp X
588f05dfde 画廊页面 2022-02-11 13:48:22 +08:00
Wisp X
6d8031b0a2 🐛 修复 BUG 2022-02-11 08:42:28 +08:00
Wisp X
5d64068456 💬 update readme 2022-02-10 17:18:42 +08:00
Wisp X
593c3bc18a 改进全屏逻辑 2022-02-10 17:03:36 +08:00
Wisp X
dddad809a0 画廊页面 2022-02-10 16:23:05 +08:00
Wisp X
0182e2f350 用户管理 2022-02-10 15:43:02 +08:00
Wisp X
ba2cff0977 🎨 改进 2022-02-10 14:58:53 +08:00
Wisp X
d090983954 🐛 Fixing a bug. 2022-02-10 14:46:35 +08:00
Wisp X
75bf3ec2f7 用户列表 2022-02-10 14:25:45 +08:00
Wisp X
0d4e9514a1 💄 改进样式 2022-02-10 13:10:31 +08:00
Wisp X
63741bd8c5 💄 改进样式 2022-02-10 13:07:36 +08:00
Wisp X
b776938146 💄 Updating the UI and style files. 2022-02-10 11:34:20 +08:00
Wisp X
ec28485e69 📦 Updating compiled files or packages. 2022-02-10 11:10:33 +08:00
Wisp X
ff81bcc5d8 🔧 Changing configuration files. 2022-02-10 11:01:37 +08:00
Wisp X
2b44ab2baa 💬 up readme.md 2022-02-09 15:54:40 +08:00
Wisp X
61c876961c 📦 升级至 laravel 9 正式版 2022-02-09 15:49:36 +08:00
Wisp X
ec5c149a8a 🐛 修复 BUG 2022-02-08 17:47:18 +08:00
Wisp X
267cd948bf 🐛 修复 BUG 2022-02-08 15:49:22 +08:00
Wisp X
175e3fe56a 💄 更新表单配色 2022-02-08 15:11:19 +08:00
Wisp X
05e5376673 保存策略时创建符号链接 2022-01-29 21:39:11 +08:00
Wisp X
0286721429 🐛 修复 BUG 2022-01-27 17:49:16 +08:00
Wisp X
654709b00f 改进结构 2022-01-27 17:43:44 +08:00
Wisp X
b4194c5bc3 🐛 修复 BUG 2022-01-27 17:18:15 +08:00
Wisp X
6f648b2e2f Introducing new features. 2022-01-27 17:11:36 +08:00
Wisp X
2cab28a229 Introducing new features. 2022-01-27 16:58:31 +08:00
Wisp X
8fd4ae7bc8 本地策略符号链接功配置 2022-01-27 15:03:23 +08:00
Wisp X
de43341a31 储存策略更新 2022-01-26 17:51:58 +08:00
Wisp X
d31d1bb8d9 储存策略更新 2022-01-26 17:48:04 +08:00
Wisp X
e42ac5de3b 储存策略添加 2022-01-26 16:40:43 +08:00
Wisp X
d3172f1258 💄 Updating the UI and style files. 2022-01-25 08:47:37 +08:00
Wisp X
3804594829 🐛 修复 BUG 2022-01-25 08:38:28 +08:00
Wisp X
79b7585e35 组件化输入框 2022-01-24 17:58:44 +08:00
Wisp X
a93424be4a 组件化输入框 2022-01-24 17:31:42 +08:00
Wisp X
746eaa8a92 策略创建表单 2022-01-24 17:00:05 +08:00
Wisp X
035e1d140b 改进组设置 2022-01-24 15:58:42 +08:00
Wisp X
ed91764fd5 改进组设置 2022-01-24 15:55:06 +08:00
Wisp X
2ea34bcb77 策略列表 2022-01-24 14:49:15 +08:00
Wisp X
2ee532b313 角色组增删改查 2022-01-23 15:48:45 +08:00
Wisp X
fbd0a59938 🐛 修复 BUG 2022-01-23 15:32:27 +08:00
Wisp X
7b57627044 🐛 修复 BUG 2022-01-23 14:20:40 +08:00
Wisp X
4975f3e1c1 完善角色组编辑页面 2022-01-22 14:19:12 +08:00
Wisp X
5662f86aae 角色组编辑页面 2022-01-22 13:16:15 +08:00
Wisp X
3ac70a772c 🐛 修复 BUG 2022-01-21 23:48:27 +08:00
Wisp X
114b6c4b42 🐛 修复 BUG 2022-01-21 23:38:02 +08:00
Wisp X
c79123b178 🔧 改进配置方式 2022-01-21 23:31:38 +08:00
Wisp X
9dadc84f21 💄 角色组页面创建样式 2022-01-21 22:47:06 +08:00
Wisp X
95fd3fc930 改进验证器 2022-01-21 15:47:18 +08:00
Wisp X
ffc2895a6c 完善角色组创建页面样式 2022-01-21 14:36:17 +08:00
Wisp X
84e9caeb71 💄 改进角色组创建页面样式 2022-01-21 11:33:09 +08:00
Wisp X
3134fc437d 🔧 Changing configuration files. 2022-01-21 09:01:15 +08:00
Wisp X
54efefbe39 💄 Updating the UI and style files. 2022-01-21 08:01:55 +08:00
Wisp X
13ac6b064d 💄 Updating the UI and style files. 2022-01-21 07:58:02 +08:00
Wisp X
5679d3876d 💄 角色组创建表单页面样式 2022-01-20 22:42:44 +08:00
Wisp X
90863c8d1c 组于策略改为多对多关系 2022-01-20 20:41:55 +08:00
Wisp X
4880935844 🐛 修复 BUG 2022-01-20 17:14:24 +08:00
Wisp X
031c0f76ee 💄 Updating the UI and style files. 2022-01-20 17:13:43 +08:00
Wisp X
0b4fd797a3 角色组列表 2022-01-20 16:48:49 +08:00
Wisp X
3507034427 🐛 修复 BUG 2022-01-20 13:11:36 +08:00
Wisp X
74d89ee9a6 管理员中间件 2022-01-20 09:20:33 +08:00
Wisp X
ef4d214a9e 🐛 修复 BUG 2022-01-20 08:04:10 +08:00
Wisp X
95fd2298e7 🐛 修复 BUG 2022-01-19 22:59:20 +08:00
Wisp X
78b87d60f0 应用上传后自动清除预览图功能 2022-01-19 22:49:45 +08:00
Wisp X
89dd7b75c7 应用用户设置的默认图片权限 2022-01-19 22:44:43 +08:00
Wisp X
615da555a5 用户设置功能 2022-01-19 22:40:25 +08:00
Wisp X
39feb32ba9 📦 Updating compiled files or packages. 2022-01-19 20:28:12 +08:00
Wisp X
d6564bbfbd 🎨 改进图片列表 2022-01-19 20:17:46 +08:00
Wisp X
cd71b194e6 改进缩略图生成算法 2022-01-19 15:05:44 +08:00
Wisp X
4e38a53971 🐛 修复动图上传后没有动画的bug 2022-01-19 13:37:34 +08:00
Wisp X
cf745ec73a 图片增加缓存 2022-01-19 13:33:10 +08:00
Wisp X
046d642a87 水印跳过 gif 动图 2022-01-19 13:09:24 +08:00
Wisp X
d56e605e35 改进试图 2022-01-19 11:37:33 +08:00
Wisp X
24a0a576e8 更换图片处理库 2022-01-19 11:00:25 +08:00
Wisp X
d8ee38573b 🐛 修复 BUG 2022-01-19 09:38:38 +08:00
Wisp X
0e8ac365a5 改进水印功能 2022-01-19 08:51:15 +08:00
Wisp X
e1c6201c7c 🐛 修复 BUG 2022-01-19 08:36:08 +08:00
Wisp X
6a0704b183 图片水印支持 2022-01-18 22:57:34 +08:00
Wisp X
526e32a5f0 增加平铺水印支持 2022-01-18 21:54:55 +08:00
Wisp X
83bee48279 文字水印支持 2022-01-18 14:16:40 +08:00
Wisp X
5d87c5dbbc 改进缩略图生成算法 2022-01-18 11:07:32 +08:00
Wisp X
34af734d01 🐛 修复 BUG 2022-01-17 22:52:40 +08:00
Wisp X
82ec6c6ac6 改进组配置,修复bug 2022-01-17 22:45:18 +08:00
Wisp X
9718871610 支持复制缩略图链接 2022-01-17 16:19:53 +08:00
Wisp X
ff2162cbe9 改进缩略图生成质量 2022-01-17 14:19:25 +08:00
Wisp X
3298e3f4d8 移除缓存增加标记 2022-01-17 13:56:50 +08:00
Wisp X
7e142dd5c6 图片缓存增加标记 2022-01-17 13:53:36 +08:00
Wisp X
7aa085d19f 原图保护功能 2022-01-17 13:49:20 +08:00
Wisp X
84a4698e97 缩略图增加缓存机制 2022-01-17 13:26:05 +08:00
Wisp X
c919c556ca 🐛 修复 BUG 2022-01-17 11:28:28 +08:00
Wisp X
a6d0b9edff 🐛 修复 BUG 2022-01-17 11:12:28 +08:00
Wisp X
8e8113af50 动态生成缩略图 2022-01-17 11:06:42 +08:00
Wisp X
c9bca05daa 原图保护功能 2022-01-17 09:59:55 +08:00
Wisp X
90eb5c71e9 策略增加原图保护功能 2022-01-17 09:09:31 +08:00
Wisp X
4cbdcf8354 📦 Updating compiled files or packages. 2022-01-17 08:31:42 +08:00
Wisp X
d59e0c7bd1 📦 安装调试工具 2022-01-15 10:49:42 +08:00
Wisp X
6cdf71dcb4 Introducing new features. 2022-01-15 10:07:36 +08:00
Wisp X
a051ccb2c6 Introducing new features. 2022-01-14 17:38:37 +08:00
Wisp X
b1e79f846c 🐛 修复 BUG 2022-01-14 16:41:26 +08:00
Wisp X
08c8ce757e 🐛 修复 BUG 2022-01-14 16:41:03 +08:00
Wisp X
6e2088f5b6 🐛 修复 BUG 2022-01-14 16:39:33 +08:00
Wisp X
6f96b298a3 🐛 修复 BUG 2022-01-14 16:38:07 +08:00
Wisp X
b38957d374 动态更新容量进度条 2022-01-14 16:26:07 +08:00
Wisp X
8e4d9f23ad 🐛 修复 BUG 2022-01-14 15:24:48 +08:00
Wisp X
8d04e56572 🐛 修复 BUG 2022-01-14 15:20:16 +08:00
Wisp X
3a1095dcb3 邮件验证功能 2022-01-14 14:28:58 +08:00
Wisp X
f12beb3fd2 🐛 修复 BUG 2022-01-14 13:17:54 +08:00
Wisp X
f3bc257a87 🐛 修复 BUG 2022-01-14 10:55:08 +08:00
Wisp X
0cba2cea6c 支持查看图片详细信息 2022-01-14 10:49:13 +08:00
Wisp X
ee1a984abf 🐛 修复 BUG 2022-01-13 17:07:09 +08:00
Wisp X
7db4615272 完善我的图片管理 2022-01-13 16:46:15 +08:00
Wisp X
fbd7470cd6 💄 移动端构建菜单 2022-01-13 15:50:51 +08:00
Wisp X
19b76634e6 💄 移动端构建菜单 2022-01-13 15:40:51 +08:00
Wisp X
5dff05f791 🐛 修复 BUG 2022-01-13 11:37:46 +08:00
Wisp X
81016e5643 图片设置权限功能 2022-01-13 11:21:21 +08:00
Wisp X
3e911db0ca 图片重命名功能 2022-01-13 10:49:56 +08:00
Wisp X
f36ba0db6b 图片重命名功能 2022-01-13 10:45:26 +08:00
Wisp X
0481188a66 🐛 修复搜索无法输入内容的BUG 2022-01-13 08:45:10 +08:00
Wisp X
839b1b5927 权限筛选 2022-01-13 08:40:56 +08:00
Wisp X
44b851d86a 📦 更新包版本 2022-01-13 08:20:07 +08:00
Wisp X
eb4c7117a1 🎨 改进模型 2022-01-12 13:49:33 +08:00
Wisp X
6464851d2e 迁移至 laravel 9 2022-01-12 13:26:39 +08:00
Wisp X
fba806d24a 更改图片服务类名 2022-01-12 08:49:46 +08:00
Wisp X
c5cf0081ae 图片删除功能 2022-01-12 08:47:21 +08:00
Wisp X
47a36f7c26 💄 Updating the UI and style files. 2022-01-06 17:59:40 +08:00
Wisp X
edb5c2a6b4 删除相册功能 2022-01-05 08:56:21 +08:00
Wisp X
a00cdb4f7f 改进 contextjs 组件 2021-12-31 17:13:15 +08:00
Wisp X
f8f9422491 改进 contextjs 组件 2021-12-31 15:06:07 +08:00
Wisp X
da077dc8bf 改进右键操作逻辑 2021-12-31 14:08:22 +08:00
Wisp X
4d96077769 💄 Updating the UI and style files. 2021-12-31 13:36:15 +08:00
Wisp X
e0b68d5470 🐛 修复 BUG 2021-12-31 10:47:10 +08:00
Wisp X
844d768fd3 完善移出相册功能 2021-12-31 09:27:10 +08:00
Wisp X
7117198e59 改进 contextjs,完善移出相册功能 2021-12-31 09:20:05 +08:00
Wisp X
10f38ad57d 图片移动到相册功能 2021-12-30 16:51:03 +08:00
Wisp X
e4823e262a Introducing new features. 2021-12-30 16:02:39 +08:00
Wisp X
f757a882d9 🐛 修复 BUG 2021-12-30 15:13:51 +08:00
Wisp X
4897e6e6c8 复制图片功能 2021-12-30 15:09:55 +08:00
Wisp X
d609d5527c 复制图片功能 2021-12-30 11:39:46 +08:00
Wisp X
cdd7269ff4 复制图片功能 2021-12-30 11:39:26 +08:00
Wisp X
facbc1269b 改进 context-js 组件 2021-12-29 17:38:58 +08:00
Wisp X
935f81c03f 完善右键菜单选项 2021-12-28 22:05:59 +08:00
Wisp X
0ecf67188a 🐛 修复 BUG 2021-12-28 20:05:09 +08:00
Wisp X
5bdd05960e Introducing new features. 2021-12-28 20:01:49 +08:00
Wisp X
62402ef8b4 🐛 Fixing a bug 2021-12-28 10:15:55 +08:00
Wisp X
91a5f606df 🐛 Fixing a bug 2021-12-27 22:36:54 +08:00
Wisp X
8b584b54e4 相册编辑功能 2021-12-26 14:26:12 +08:00
Wisp X
6a4a57aa65 ajax替换为axios 2021-12-26 13:56:02 +08:00
Wisp X
b25272346c 文件夹创建功能 2021-12-26 13:12:08 +08:00
Wisp X
fe52158d8e 文件夹创建功能 2021-12-26 12:16:36 +08:00
Wisp X
173cf43619 🐛 修复 BUG 2021-12-25 23:32:46 +08:00
Wisp X
013a71234b 完善相册样式 2021-12-25 23:29:53 +08:00
Wisp X
f326f74b76 改进鼠标选框 2021-12-25 09:18:31 +08:00
Wisp X
5cd405ea7c 我的图片鼠标选框 2021-12-25 01:38:58 +08:00
Wisp X
50d79fc2c2 完成相册筛选 2021-12-24 15:52:18 +08:00
Wisp X
7c5e52591f 🐛 修复 BUG 2021-12-24 14:43:07 +08:00
Wisp X
3b6971d648 🐛 修复 BUG 2021-12-24 09:15:04 +08:00
Wisp X
fdb55f1c2b 🎨 改进 ajax 拦截器 2021-12-24 08:51:01 +08:00
Wisp X
15685d664f 我的图片支持关键字搜索 2021-12-23 22:49:40 +08:00
Wisp X
bb78e935a8 我的图片支持排序 2021-12-23 22:23:30 +08:00
Wisp X
f4a1064b15 我的图片支持排序 2021-12-23 22:13:10 +08:00
Wisp X
09df3deeea 封装上拉加载组件 2021-12-23 21:44:25 +08:00
Wisp X
55ece284da 💄 Updating the UI and style files. 2021-12-23 08:54:45 +08:00
Wisp X
b67ae153f9 💄 我的图片页面抽屉组件 2021-12-22 14:25:30 +08:00
Wisp X
aca4acbc8e 💄 改进样式 2021-12-22 12:33:23 +08:00
Wisp X
dde30bde77 💄 Updating the UI and style files. 2021-12-22 08:27:11 +08:00
Wisp X
035c184761 💄 完善我的图片页面选择样式 2021-12-21 23:16:31 +08:00
Wisp X
44d096c8ed 上传图片时储存图片宽高 2021-12-21 09:09:54 +08:00
Wisp X
708d3e5137 🐛 修复 BUG 2021-12-20 22:59:08 +08:00
Wisp X
f3b5840c3e Revert "💄 Updating the UI and style files."
This reverts commit 6c61ccc3e4.
2021-12-20 22:49:03 +08:00
Wisp X
e158be2ce2 Introducing new features. 2021-12-20 22:07:00 +08:00
Wisp X
9842338d25 其他改进 2021-12-20 22:05:35 +08:00
Wisp X
cbe0f58f2e 取消自动更新检测 2021-12-20 22:01:55 +08:00
Wisp X
10073b8f98 取消自动更新检测 2021-12-20 22:00:03 +08:00
Wisp X
c2ae231293 📦 更新 phpmailer/phpmailer 2021-12-20 21:56:19 +08:00
Wisp X
64e328f6b3 增加一键复制功能 Closed #167 2021-12-20 21:54:41 +08:00
Wisp X
33af412423 更新 api 文档 2021-12-20 21:32:38 +08:00
Wisp X
fc385d49a8 显示用户的注册时间 2021-12-20 21:29:15 +08:00
Wisp X
08982260fb 改进登录方式 2021-12-20 21:27:25 +08:00
Wisp X
4689af7768 📦 将第三方资源放置本地 2021-12-20 21:23:21 +08:00
WispX
65baffe44e 📦 将第三方资源放置本地 2021-12-20 18:58:54 +08:00
WispX
16ac2b8144 📦 将第三方资源放置本地 2021-12-20 18:33:23 +08:00
WispX
8ad22cc147 📦 将第三方资源放置本地 2021-12-20 18:23:05 +08:00
WispX
6c61ccc3e4 💄 Updating the UI and style files. 2021-12-20 11:13:22 +08:00
WispX
3289b9d688 💄 Updating the UI and style files. 2021-12-20 08:56:20 +08:00
WispX
4518bfdbce 💄 完善我的图片页面 2021-12-19 22:31:14 +08:00
WispX
01b4502c45 💄 完成我的图片页面基础样式 2021-12-19 16:06:48 +08:00
WispX
68a55a8617 💄 Updating the UI and style files. 2021-12-18 21:08:43 +08:00
WispX
4a9715d77f 仪表盘数据展示 2021-12-18 16:12:58 +08:00
WispX
d229ef18a8 永久缓存系统配置 2021-12-17 23:39:22 +08:00
WispX
cdd40511ea 完善文件命名规则方法 2021-12-17 23:29:50 +08:00
WispX
2019fb86b7 🐛 修复 BUG 2021-12-17 16:09:48 +08:00
WispX
9ee8d51852 🐛 修复 BUG 2021-12-17 16:02:36 +08:00
WispX
bbdad3f911 改进上传服务 2021-12-17 15:57:01 +08:00
WispX
7505616e3f 改进上传服务 2021-12-17 15:55:30 +08:00
WispX
d795171461 完善文件上传服务 2021-12-17 14:50:41 +08:00
WispX
f055126a69 完善文件上传服务 2021-12-17 14:50:09 +08:00
WispX
970205e56d 完善文件上传服务 2021-12-17 14:35:35 +08:00
WispX
010c33f18c 完善文件上传服务 2021-12-17 14:28:16 +08:00
WispX
3f6df9d2a0 文件上传功能 2021-12-17 11:37:34 +08:00
WispX
1da4a536ba 完善策略配置 2021-12-16 22:47:00 +08:00
WispX
9bd446e822 获取配置方法,缓存 1 天 2021-12-16 11:26:09 +08:00
WispX
5efd4f5b71 获取配置方法,缓存 1 天 2021-12-16 11:02:33 +08:00
WispX
5d7e5a3cad 初始化配置 seed 2021-12-16 10:25:42 +08:00
WispX
06fa1e5636 💄 配置常量 2021-12-15 22:44:49 +08:00
WispX
ac1b39070a 💄 完善上传组件 2021-12-15 20:41:58 +08:00
WispX
8040d0de1f 💄 完善上传组件 2021-12-15 19:39:14 +08:00
WispX
3282c62b22 💄 替换上传组件 2021-12-14 23:20:52 +08:00
WispX
f91c4b2fbf 💄 Updating the UI and style files. 2021-12-14 11:02:15 +08:00
WispX
e2e0a2ce9a Introducing new features. 2021-12-13 22:46:05 +08:00
WispX
edc61f42d9 💄 Updating the UI and style files. 2021-12-13 21:35:43 +08:00
WispX
80e8682a5f 🗃️ 图片表增加访问权限字段 2021-12-13 21:27:16 +08:00
WispX
c5de08c00b 完善未登录状态首页样式 2021-12-13 20:56:06 +08:00
WispX
7fded3473a 🐛 Fixing a bug. 2021-12-13 19:55:39 +08:00
WispX
b7788d0e5e 🔧 Changing configuration files. 2021-12-13 10:22:39 +08:00
WispX
bdae76cadb 💄 复制全部链接功能 Closed #167 2021-12-13 10:11:07 +08:00
WispX
2cca08a2cd 💄 完善上传组件 2021-12-13 09:45:02 +08:00
WispX
fd863c8a4a 💄 完善上传组件 2021-12-13 09:23:31 +08:00
WispX
dc8189335f 💄 完善上传组件 2021-12-12 21:54:48 +08:00
WispX
73742f886e 💄 完善上传组件 2021-12-12 20:57:10 +08:00
WispX
617273a9a4 💄 改进样式 2021-12-12 15:40:42 +08:00
WispX
8f03aebcdc 💄 改进样式 2021-12-12 15:37:34 +08:00
WispX
4466003203 上传组件基础样式 2021-12-12 15:23:08 +08:00
WispX
dc91c49713 完善模型 2021-12-11 21:50:32 +08:00
WispX
8853cac38d 重新设计表结构 2021-12-11 20:06:04 +08:00
WispX
874a554829 页面基本布局、登录、注册、找回密码 2021-12-11 17:24:44 +08:00
WispX
9800d5df07 🎉 install laravel framework. 2021-12-10 09:11:15 +08:00
WispX
82988ebe2e 🔧 Changing configuration files. 2021-09-23 11:26:42 +08:00
Redy
39b1ac1705 用户管理和用户设置前台显示创建时间 (#263) 2021-09-23 11:19:17 +08:00
WispX
e7a08e5932 🐛 修复 BUG 2021-07-30 17:09:35 +08:00
WispX
b8de26f515 🐛 修复 BUG 2021-07-28 10:59:34 +08:00
WispX
11327ed215 🐛 修复 BUG 2021-07-28 10:35:59 +08:00
WispX
fbbc69f80a 🎨 改进登录方式 2021-07-28 10:27:22 +08:00
WispX
e4e7e57580 🐛 修复 BUG 2021-07-28 10:21:39 +08:00
WispX
c5bed1e7a4 🎨 Improve performance 2021-07-28 10:14:48 +08:00
WispX
83d794acb4 🎨 Improve performance 2021-07-28 10:02:34 +08:00
WispX
635d05e217 国际化 2021-07-27 10:53:27 +08:00
WispX
8e3179ba42 Update bug_report.md 2021-07-27 09:41:56 +08:00
WispX
5f7d056a77 Merge branch 'master' of https://github.com/wisp-x/lsky-pro into dev 2021-07-27 09:41:03 +08:00
Wisp X
5147586776 Update bug_report.md 2021-07-27 09:39:55 +08:00
Wisp X
2a00978441 Update issue templates 2021-07-27 09:36:25 +08:00
WispX
b8a8174d0c 多语言开关 2021-07-27 09:24:40 +08:00
WispX
89f537c4e2 多语言开关 2021-07-27 09:24:20 +08:00
WispX
d09d57dbe1 Add Multilingualism. Closed #145 2021-07-26 17:58:17 +08:00
WispX
c09bece154 🐛 Fixed a bug that failed to send a password recovery email 2021-07-26 14:55:56 +08:00
WispX
0c0b60f86c 🐛 修复 BUG 2021-07-26 14:18:18 +08:00
WispX
77cbabf20b 🐛 修复 BUG 2021-07-26 13:38:15 +08:00
WispX
f951a2bd06 模板多语言 2021-07-26 13:25:43 +08:00
WispX
7b5634b439 模板多语言 2021-07-26 13:23:26 +08:00
WispX
b704a2c30a Adaptive Multilingualism. 2021-07-23 17:46:26 +08:00
WispX
fbea72c239 📦 Updating compiled files or packages. 2021-07-22 09:41:02 +08:00
WispX
5565b02991 Create FUNDING.yml 2021-07-21 16:57:20 +08:00
WispX
876e45b55c Merge branch 'master' into dev 2021-07-21 16:56:57 +08:00
WispX
8afd8b4f21 Create FUNDING.yml 2021-07-21 16:56:40 +08:00
WispX
6377815540 Create FUNDING.yml 2021-07-21 16:56:10 +08:00
WispX
b5ae9bbe90 Create FUNDING.yml 2021-07-21 16:55:10 +08:00
WispX
4708057b8b Merge branch 'master' of https://github.com/wisp-x/lsky-pro into dev 2021-07-21 16:48:21 +08:00
Wisp X
edcadf1c3c Create FUNDING.yml 2021-07-21 16:45:02 +08:00
WispX
5c3d5b7ec4 🐛 Fixed The undefined folder. Fixed #240 2021-07-21 13:39:15 +08:00
WispX
6226f4b445 🐛 Fixed picture management page turning bug. Closed #236 2021-07-19 17:04:41 +08:00
Wisp X
0688d1192c ⬆️ Change version 2021-07-17 22:00:40 +08:00
Licoy
f1d30f03c1 增加相同ip一天内最大上传限制功能 2021-07-17 22:00:40 +08:00
dependabot[bot]
f53fed14f7 ⬆️ Bump phpmailer/phpmailer from 6.4.1 to 6.5.0
Bumps [phpmailer/phpmailer](https://github.com/PHPMailer/PHPMailer) from 6.4.1 to 6.5.0.
- [Release notes](https://github.com/PHPMailer/PHPMailer/releases)
- [Changelog](https://github.com/PHPMailer/PHPMailer/blob/master/changelog.md)
- [Commits](https://github.com/PHPMailer/PHPMailer/compare/v6.4.1...v6.5.0)

---
updated-dependencies:
- dependency-name: phpmailer/phpmailer
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-24 08:26:36 +08:00
WispX
e5fce6f1dd 💬 update readme.md 2021-06-09 13:06:34 +08:00
WispX
28e8f8dc2a 💄 更改鉴黄内容评级的选项描述 2021-05-19 08:31:01 +08:00
WispX
af52759a1b Merge branch 'master' into dev 2021-05-18 13:44:06 +08:00
WispX
d51c4e6d3e 🐛 修复 BUG 2021-05-18 13:43:54 +08:00
WispX
774cba8658 Merge branch 'master' into dev 2021-05-18 13:42:37 +08:00
dependabot[bot]
4c789dd99a ⬆️ Bump phpmailer/phpmailer from 6.4.0 to 6.4.1
Bumps [phpmailer/phpmailer](https://github.com/PHPMailer/PHPMailer) from 6.4.0 to 6.4.1.
- [Release notes](https://github.com/PHPMailer/PHPMailer/releases)
- [Changelog](https://github.com/PHPMailer/PHPMailer/blob/master/changelog.md)
- [Commits](https://github.com/PHPMailer/PHPMailer/compare/v6.4.0...v6.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-05 15:13:16 +08:00
WispX
bde8217a6d 🐛 修复 BUG 2021-04-22 16:00:30 +08:00
WispX
de90d79fbd 🐛 修复 BUG 2021-04-21 17:35:17 +08:00
WispX
089f4fdde8 💬 Updating readme.md. 2021-04-21 16:38:44 +08:00
WispX
e985da4492 Introducing new features. 2021-04-21 16:33:55 +08:00
WispX
1fc8835018 💬 Updating readme.md. 2021-04-21 16:14:58 +08:00
WispX
2ed547a735 Added automatic watermark features. Closes (#196, #186, #138, #85, #75) 2021-04-21 16:12:55 +08:00
WispX
5e4d4a7161 The token is shown in the materials. Closed #157 2021-04-21 09:51:49 +08:00
WispX
d9065778aa The token is shown in the materials. Closed 157 2021-04-21 09:50:38 +08:00
WispX
749583a11e 💄 Updating the UI and style files. 2021-04-21 09:07:08 +08:00
WispX
9a54fcaa52 💄 Updating the UI and style files. 2021-04-21 09:04:03 +08:00
WispX
271981d0f0 💄 Updating the UI and style files. 2021-04-21 08:59:01 +08:00
WispX
5231f8d541 Added gallery display feature. 2021-04-20 10:35:33 +08:00
WispX
703d686d5e 🐛 Fixed the bug of background IP interface failure. Closed #179 2021-04-19 15:24:14 +08:00
WispX
989f0d323a 💄 Modify the link of public security record system. 2021-04-16 17:39:36 +08:00
WispX
1bbd06cb28 Upload return image id. Closed #180 2021-04-16 17:29:10 +08:00
WispX
26840ede07 📦 Compatible with PHP8. Closed #192 2021-04-16 16:57:16 +08:00
WispX
1a4a317faf 🐛 Fixed image paste naming problem. Closed #194 2021-04-16 16:52:36 +08:00
WispX
d30a29e89e 🐛 Fixing a bug. 2021-01-21 11:04:51 +08:00
WispX
b23fc23788 上传接口返回ID 2020-11-20 14:15:43 +08:00
WispX
97b2eafdae 原始文件名命名规则支持与其他规则组合 2020-08-11 15:52:07 +08:00
WispX
a4bbadaa88 文件路径命名规则可为空。Closed #159 2020-08-11 15:24:50 +08:00
WispX
931c546257 文件路径命名规则可为空。Closed #159 2020-08-11 14:49:03 +08:00
WispX
32aafe5f1b 🐛 Fixing a bug. 2020-08-11 14:34:35 +08:00
WispX
a828a16890 🐛 Fixing a bug. Closed #156 2020-08-11 14:28:06 +08:00
WispX
02adeab97f 🐛 Fixing a bug. Closed #156 2020-08-11 14:15:20 +08:00
WispX
676a1efa7e 💬 UPDATE README.MD 2020-08-11 14:12:01 +08:00
WispX
7721e49a78 🐛 Fixing a bug. 2020-07-15 11:39:21 +08:00
WispX
678ba4f73d 更改后台图片管理每页显示数量为 25 条 2020-07-15 11:36:16 +08:00
WispX
d68c671d48 📌 Pinning dependencies to specific versions. 2020-07-15 11:27:53 +08:00
WispX
8093181585 💄 Updating the UI and style files. 2020-07-15 11:05:18 +08:00
WispX
a12ef5fd60 取消 coding 托管仓库。 2020-07-15 10:41:23 +08:00
WispX
9bdad2aeae 📦 Updating compiled files or packages. 2020-07-15 10:38:33 +08:00
WispX
db995622a9 增加八位数随机字符串命名规则. Closed #121 2020-06-11 17:59:38 +08:00
WispX
d39e40a4ba 🐛 修复后台图片列表切换 select 后点击下一页数据被重置的 bug. Closed #128 2020-06-11 17:56:15 +08:00
WispX
e88ef59b7b Merge branches 'dev' and 'master' of https://github.com/wisp-x/lsky-pro into dev 2020-06-11 17:04:13 +08:00
dependabot[bot]
bfe9e322aa ⬆️ Bump phpmailer/phpmailer from 6.1.4 to 6.1.6
Bumps [phpmailer/phpmailer](https://github.com/PHPMailer/PHPMailer) from 6.1.4 to 6.1.6.
- [Release notes](https://github.com/PHPMailer/PHPMailer/releases)
- [Changelog](https://github.com/PHPMailer/PHPMailer/blob/master/changelog.md)
- [Commits](https://github.com/PHPMailer/PHPMailer/compare/v6.1.4...v6.1.6)

Signed-off-by: dependabot[bot] <support@github.com>
2020-05-29 06:55:46 +08:00
WispX
2ffb2dc8b8 💄 Updating the UI and style files. 2020-04-22 08:39:55 +08:00
WispX
9e6f1d4025 Introducing new features. Closed #73 2020-04-21 22:03:22 +08:00
WispX
f35dfcea36 🐛 修复用户图片列表中图片分页后无法搜索到指定图片的 bug. Closed #99 2020-04-21 21:09:14 +08:00
WispX
b14e790fbb 🐛 修复自定义链接参数后上传返回的链接中出现重复参数的 bug. Closed #118 2020-04-21 21:04:59 +08:00
WispX
d9c6cf4b5c 🐛 Fixing a bug. Closed #112 2020-04-21 18:00:13 +08:00
WispX
6ba708143d 🐛 Fixing a bug. 2020-03-19 22:22:39 +08:00
WispX
eac3cade5c 改进用户注册验证条件 2020-03-19 16:48:32 +08:00
WispX
dc78ef18af 💬 Updating text and literals. 2020-03-19 16:36:15 +08:00
WispX
62b0ee5e2b 🐛 Fixing a bug. 2020-03-19 16:20:41 +08:00
WispX
ca06b2ffab 🐛 Fixing a bug. 2020-03-19 13:47:57 +08:00
WispX
98b3b2da71 🐛 Fixing a bug. 2020-03-19 13:46:49 +08:00
WispX
f93fe52835 🐛 修复 BUG 2020-03-19 13:38:57 +08:00
WispX
f8c4329f36 🐛 修复 BUG 2020-03-19 13:33:19 +08:00
WispX
3dce804566 📝 更新文档地址 2020-03-19 12:12:44 +08:00
WispX
1022c23d59 改进更新功能 2020-03-19 12:06:36 +08:00
WispX
12bca3ec43 改进更新功能 2020-03-19 11:36:10 +08:00
WispX
bb7ebf1e81 改进更新功能 2020-03-19 10:46:26 +08:00
WispX
cb5f2f8bce 🐛 Fixing a bug. 2020-03-19 07:37:26 +08:00
WispX
35e7cb51a7 🎨 改进更新 2020-03-19 07:25:25 +08:00
WispX
e4154d0723 Introducing new features. 2020-03-18 22:31:35 +08:00
WispX
8ee4598a9c 💄 Updating the UI and style files. 2020-03-18 22:20:30 +08:00
WispX
544149637f 💄 Updating the UI and style files. 2020-03-18 22:14:09 +08:00
WispX
50348ab7d6 💄 Updating the UI and style files. 2020-03-18 22:08:45 +08:00
WispX
8d735df6a8 封禁 IP 功能支持通配符 2020-03-18 21:18:36 +08:00
WispX
32aac46ff0 🔖 Releasing / Version tags. 2020-03-18 20:12:34 +08:00
WispX
531b95ca33 💄 Updating the UI and style files. 2020-03-18 16:59:16 +08:00
WispX
5d2633c49f 💄 Updating the UI and style files. 2020-03-18 16:32:24 +08:00
WispX
9266139c25 💩 改进单用户模式 2020-03-18 14:33:37 +08:00
WispX
f8be031d08 增加单用户站点模式 2020-03-18 14:27:30 +08:00
WispX
ef797c2847 增加单用户站点模式 2020-03-18 14:25:36 +08:00
WispX
807c96d68b 💬 更新 README.md 2020-03-18 10:05:37 +08:00
WispX
9eda2ef204 🐛 修复 BUG 2020-03-18 09:54:12 +08:00
WispX
338ef83f73 🔧 去除 mysqli 安装条件, 换成 PDO 2020-03-18 09:43:55 +08:00
WispX
629078637b 💬 更新 README.md 2020-03-18 09:24:42 +08:00
WispX
d71b444de8 改进升级功能 2020-03-18 09:23:15 +08:00
WispX
00c2f3a979 更新时不覆盖环境变量文件 2020-03-18 09:22:33 +08:00
WispX
47d6a60f4c 增加IP封禁功能 2020-03-18 09:11:14 +08:00
WispX
d30d0c6960 💬 Updating text and literals. 2020-03-17 17:53:04 +08:00
WispX
e1703f1146 增加图片自定义额外参数功能 2020-03-17 17:50:46 +08:00
WispX
dcc14a1493 增加图片自定义额外参数功能 2020-03-17 17:46:11 +08:00
WispX
da4556bf48 🎨 改进 2020-03-17 16:32:48 +08:00
WispX
4b60767b0a FTP 增加被动模式 2020-03-17 15:58:40 +08:00
WispX
3a236746c4 💄 Updating the UI and style files. 2020-03-17 14:56:38 +08:00
WispX
b6a25939b2 💄 Updating the UI and style files. 2020-03-17 14:54:59 +08:00
WispX
fba0ee18cc 🐛 修复 BUG 2020-03-17 14:48:50 +08:00
WispX
659c750645 🐛 修复 BUG 2020-03-17 14:47:27 +08:00
WispX
ec25acbfbb 💄 改进接口文档 2020-03-17 14:41:17 +08:00
WispX
8a27933d44 🐛 修复 BUG 2020-03-17 14:36:38 +08:00
WispX
767f6a49df 🐛 修复 BUG 2020-03-17 14:29:53 +08:00
WispX
d90cacb33a 🐛 修复 BUG 2020-03-17 14:27:36 +08:00
WispX
dd927972af ♻️ 重构接口权限认证方式 2020-03-17 14:13:06 +08:00
WispX
d4ba4648b4 🐛 修复 BUG 2020-03-17 13:22:33 +08:00
WispX
af5bd11c8f 🎨 改进中间件 2020-03-17 13:18:36 +08:00
WispX
54627d6647 ♻️ 重构 web 端鉴权方式 2020-03-17 11:32:37 +08:00
WispX
c2d20536a3 🐛 修复移动图片要根文件夹会提示不存在文件夹的 bug 2020-03-16 20:14:09 +08:00
WispX
56460b57d5 💄 Updating the UI and style files. 2020-03-16 17:15:31 +08:00
WispX
42874d3c58 💄 改进图片列表多选操作 2020-03-16 17:01:44 +08:00
WispX
fd766552f7 💄 改进用户列表多选操作 2020-03-16 16:53:49 +08:00
WispX
92355bd765 💄 改进角色组新增方式 2020-03-16 16:17:25 +08:00
WispX
3e083ff453 🎨 改进 token 生成方式 2020-03-16 14:57:41 +08:00
WispX
243587450b 🎨 改进安装 2020-03-16 14:00:03 +08:00
WispX
e1cab9e44c 💬 Updating text and literals. 2020-03-16 09:17:44 +08:00
WispX
b165545f8e .🙈 Adding or updating a .gitignore file. 2020-03-16 09:14:18 +08:00
WispX
2a30fd56d8 💬 Updating text and literals. 2020-03-16 07:34:15 +08:00
WispX
9ffb41f047 💬 Updating text and literals. 2020-03-16 07:12:23 +08:00
WispX
c6d04f8a94 💬 Updating text and literals. 2020-03-16 07:12:04 +08:00
WispX
3d4eb3ed64 💬 up 2020-03-16 07:08:40 +08:00
WispX
4593801d22 🐛 修复 BUG 2020-03-15 23:06:39 +08:00
WispX
ca08b610d7 💄 改进细节样式 2020-03-15 22:44:17 +08:00
WispX
5c0cfeb6a1 💬 更新 README.md 2020-03-15 21:17:55 +08:00
WispX
4f8d57f052 💬 更新 README.md 2020-03-15 21:11:09 +08:00
WispX
4bd6410e86 💄 改进 dark 主题 2020-03-15 20:54:28 +08:00
WispX
27046d1556 💄 改进上传按钮 2020-03-15 20:37:32 +08:00
WispX
0eb0169701 改进 2020-03-15 20:29:09 +08:00
WispX
b328d4bf30 增加 dark 模式(小声bb, 微信什么时候出) 2020-03-15 20:19:56 +08:00
WispX
e136b24314 💄 改进控制台样式 2020-03-15 18:25:11 +08:00
WispX
fec1fafb3e 🍱 更改静态文件源(jsdelivr) 2020-03-15 17:52:08 +08:00
WispX
6296a78ec4 💬 更新 README.md 2020-03-15 17:09:21 +08:00
WispX
d5bfb689c4 🗃️ 更新数据库 2020-03-15 16:43:57 +08:00
WispX
0f7fedc66e 🙈 Adding or updating a .gitignore file. 2020-03-15 16:39:09 +08:00
WispX
3468a901f4 🍱 删除本地静态资源, 使用第三方 CDN 2020-03-15 16:36:40 +08:00
WispX
666b98db07 💬 更新 README.md 2020-03-15 14:52:09 +08:00
WispX
980b503dd7 💬 更新 README.md 2020-03-15 14:21:33 +08:00
WispX
069514469d 增加升级前询问是否备份原系统的模态框 2020-03-15 13:28:58 +08:00
WispX
e6cbaa88c7 升级系统备份时忽略 public 目录 2020-03-15 12:47:28 +08:00
WispX
a6af81c800 🎨 完善升级成功后的提示 2020-03-15 12:15:37 +08:00
WispX
28ba9d0c02 🎨 改进更新出现错误的提示 2020-03-15 11:57:43 +08:00
WispX
132a656e90 .🙈 更新 .gitignore 文件 2020-03-15 11:52:37 +08:00
WispX
726d5163db 改进备份系统功能 2020-03-15 11:39:40 +08:00
WispX
aac0d26614 🐛 修复 BUG 2020-03-15 10:43:43 +08:00
WispX
f52ffac078 🐛 修复 BUG 2020-03-15 10:39:02 +08:00
WispX
00376401fc 🐛 修复每次下载安装包 md5 值都不一致的 bug 2020-03-15 10:14:20 +08:00
WispX
785e49b7d5 Revert "🗃️ up"
This reverts commit 5c64eaeb
2020-03-15 08:43:42 +08:00
WispX
5c64eaeb4e 🗃️ up 2020-03-15 08:42:49 +08:00
WispX
50dabc2f48 完成平滑升级系统功能 2020-03-15 08:28:37 +08:00
WispX
9409866bb9 更改更新方式 2020-03-15 01:09:59 +08:00
WispX
4d28c60cb8 重构安装方式 2020-03-14 23:13:03 +08:00
WispX
c36831ccea 图片最大上传限制兼容字节其他单位 Closed #91 2020-03-14 20:33:53 +08:00
WispX
72f3ad447e 📦 使用 5.0.1 版本的 bootstrap-fileinput 2020-03-14 20:22:50 +08:00
WispX
956a7d8966 安装条件增加 ZipArchive 2020-03-14 19:48:27 +08:00
WispX
7effc2c6d6 去除旧版更新数据库结构方法 2020-03-14 19:42:06 +08:00
WispX
3915f83883 🐛 修复 BUG 2020-03-14 19:26:23 +08:00
WispX
e3ca844893 完成平滑升级功能 2020-03-14 17:59:14 +08:00
WispX
5a41b1dc1e 完成平滑升级功能 2020-03-14 17:45:22 +08:00
WispX
2cb60d56c0 ⬆️ Upgrading dependencies. 2020-03-14 11:48:44 +08:00
WispX
e93e0f3675 Merge branch 'dev' of https://github.com/wisp-x/lsky-pro into dev
 Conflicts:
	extend/Upgrade.php
2020-03-14 08:19:09 +08:00
WispX
b719e6019a 🍱 Adding or updating assets. 2020-03-13 22:37:22 +08:00
WispX
3331dcd385 ⬆️ Upgrading dependencies. 2020-03-13 17:57:07 +08:00
WispX
a34aa85c39 💬 Updating text and literals. 2020-03-13 09:29:41 +08:00
WispX
71f16228ce 📌 Pinning dependencies to specific versions. 2020-03-13 00:22:29 +08:00
WispX
e62912a267 📌 Pinning dependencies to specific versions. 2020-03-13 00:19:45 +08:00
WispX
bf173ccacd 🐛 修复 BUG 2020-03-13 00:17:29 +08:00
WispX
a8cf1aab4e 🎨 改进结构和代码格式 2020-03-07 14:44:02 +08:00
WispX
c44e9d266d 支持根据别名搜索图片 2020-03-07 14:30:20 +08:00
WispX
aa43585622 🐛 修复相同 md5 的文件,删除一条记录会将文件一并删除的 bug. 2020-03-04 20:45:48 +08:00
WispX
28d6f5d670 🐛 修复删除没有会员的角色组会失败的 bug. 2020-03-03 21:11:43 +08:00
WispX
3823c9c2ee 💬 Updating text and literals. 2020-03-02 09:36:22 +08:00
WispX
4ee24754fa 🔖 Releasing / Version tags. 2020-03-02 07:39:58 +08:00
WispX
c56dc34a94 🐛 修复 BUG 2020-03-02 07:34:20 +08:00
WispX
94f9f83cf2 🐛 修复 BUG 2020-03-02 07:28:38 +08:00
WispX
0a254dc22c Merge branch 'dev' 2020-03-02 07:22:23 +08:00
WispX
4af1db0b18 🐛 修复接口上传出现错误的 bug fix #83 2020-03-02 07:21:35 +08:00
WispX
c120f78a67 Merge branch 'dev' 2020-03-01 00:05:49 +08:00
WispX
f79128866a 🔖 更新版本号 2020-03-01 00:05:26 +08:00
WispX
2479a81bde Merge branch 'dev' 2020-03-01 00:02:45 +08:00
WispX
c0ecd8ab8a 📦 Updating compiled files or packages. 2020-03-01 00:01:54 +08:00
WispX
b943d46930 Merge branch 'dev' 2020-02-29 23:55:24 +08:00
WispX
ad1fe2ca1e Introducing new features. 2020-02-29 23:53:57 +08:00
WispX
c8e4848620 删除文件不处理储存策略删除接口返回值 2020-02-29 23:45:40 +08:00
WispX
3576d40dc9 🐛 修复 BUG 2020-02-29 23:38:33 +08:00
WispX
e3880f50e7 增加用户名登录 2020-02-29 23:06:16 +08:00
WispX
e7fa6fac11 增加文件别名功能 2020-02-29 22:26:49 +08:00
WispX
5edea2cda8 增加文件别名功能 2020-02-29 22:23:38 +08:00
WispX
9cbc0debe9 🎨 改进结构和代码格式 2020-02-29 21:25:43 +08:00
WispX
1d93a5b691 🎨 改进结构和代码格式 2020-02-29 21:08:22 +08:00
WispX
b20b92c9c2 增加图片粘贴上传功能(Closed #69) 2020-02-29 20:59:01 +08:00
WispX
2f67623520 🔥 Removing Update.php 2020-02-29 19:45:51 +08:00
WispX
83cbc57838 上传的图片已存在将会直接返回该图片地址 (Closed #30)
通过文件md5校验,上传的图片已存在将会直接返回该图片地址,图片不会重复上传
2020-01-08 13:23:13 +08:00
WispX
406040d9d4 上传的图片已存在将会直接返回该图片地址 (#30)
通过文件md5校验,上传的图片已存在将会直接返回该图片地址,图片不会重复上传
2020-01-08 11:46:13 +08:00
WispX
58b5956a15 📦 更新拓展包及框架 2020-01-08 11:24:42 +08:00
WispX
08166e2044 改进 2020-01-08 11:20:12 +08:00
WispX
4b7d34fc4f ⬆️ Upgrading dependencies. 2020-01-08 11:11:07 +08:00
WispX
53c62f6e9b 📄 更新支付宝二维码 2019-11-25 11:23:52 +08:00
WispX
eb30c5320d 📄 更新支付宝二维码 2019-11-25 11:22:12 +08:00
WispX
b838d955f5 📄 更新微信二维码 2019-11-25 11:19:27 +08:00
WispX
20dfc30d63 📄 更新微信二维码 2019-11-25 11:16:38 +08:00
WispX
e6229d18ae Merge branches 'dev' and 'master' of https://github.com/wisp-x/lsky-pro into dev 2019-11-25 11:12:06 +08:00
WispX
3f79af5171 📄 增加捐赠选项 2019-11-25 11:10:09 +08:00
WispX
17fca67b2e 📄 增加捐赠选项 2019-11-25 11:07:34 +08:00
ctaoist
78dce8076d 修改 ('album') 为 input('album') 2019-11-21 10:55:28 +08:00
ctaoist
dce1376b70 添加上传到相册功能 2019-11-21 10:55:28 +08:00
1881 changed files with 64580 additions and 276228 deletions

View File

@@ -8,8 +8,11 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.{html, less, js}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

39
.env.example Normal file
View File

@@ -0,0 +1,39 @@
APP_NAME="Lsky Pro"
APP_ENV=prod
APP_KEY=
APP_DEBUG=false
APP_URL=http://localhost
LOG_CHANNEL=daily
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=
DB_HOST=
DB_PORT=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=public
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
IGNITION_SHARING_ENABLED=false

10
.gitattributes vendored Normal file
View File

@@ -0,0 +1,10 @@
* text=auto
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: https://github.com/wisp-x/lsky-pro#-%E6%8D%90%E8%B5%A0

35
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Lsky-pro
on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]
jobs:
lsky-pro-tests:
runs-on: ubuntu-latest
steps:
- uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e
with:
php-version: '8.0'
- uses: actions/checkout@v2
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Generate key
run: php artisan key:generate
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Create Database
run: |
mkdir -p database
touch database/database.sqlite
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite
DB_DATABASE: database/database.sqlite
run: vendor/bin/phpunit

51
.gitignore vendored
View File

@@ -1,33 +1,20 @@
# General
.DS_Store
.AppleDouble
.LSOverride
.idea
.vscode
runtime
/config/db.php
/installed.lock
/upgrading.lock
/*.zip
/node_modules
/public/hot
/public/storage
/public/js/custom.js
/storage/*.key
/vendor
.env
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
.env.backup
.phpunit.result.cache
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
/.idea
/.vscode
/public/i

13
.styleci.yml Normal file
View File

@@ -0,0 +1,13 @@
php:
preset: laravel
version: 8
disabled:
- no_unused_imports
finder:
not-name:
- index.php
js:
finder:
not-name:
- webpack.mix.js
css: true

View File

@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Lsky Pro Copyright (C) 2018 熊二哈
Lsky Pro Copyright (C) 2018 Wisp X
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
[http://www.gnu.org/philosophy/why-not-lgpl.html].
[http://www.gnu.org/philosophy/why-not-lgpl.html].

190
README.md
View File

@@ -1,123 +1,101 @@
<p align="center">
<a href="./public/static/app/images/logo.png" target="_blank">
<img width="300" src="./public/static/app/images/logo.png">
</a>
</p>
<img align="right" width="100" src="https://avatars.githubusercontent.com/u/100565733?s=200" alt="Lsky Pro Logo"/>
# Lsky Pro - Your photo album on the cloud.
<h1 align="left"><a href="https://www.lsky.pro">Lsky Pro</a></h1>
[官网](https://www.lsky.pro) &nbsp;
[手册](https://www.kancloud.cn/wispx/lsky-pro) &nbsp;
[演示](https://pic.iqy.ink) &nbsp;
[Chrome 拓展](https://github.com/wisp-x/lsky-pro-chrome-extension)
☁ Your photo album on the cloud.
[![PHP](https://img.shields.io/badge/PHP->=8.0-orange.svg)](http://php.net)
[![Release](https://img.shields.io/github/v/release/lsky-org/lsky-pro)](https://github.com/lsky-org/lsky-pro/releases)
[![Issues](https://img.shields.io/github/issues/lsky-org/lsky-pro)](https://github.com/lsky-org/lsky-pro/issues)
[![Code size](https://img.shields.io/github/languages/code-size/lsky-org/lsky-pro?color=blueviolet)](https://github.com/lsky-org/lsky-pro)
[![Repo size](https://img.shields.io/github/repo-size/lsky-org/lsky-pro?color=eb56fd)](https://github.com/lsky-org/lsky-pro)
[![Last commit](https://img.shields.io/github/last-commit/lsky-org/lsky-pro/dev)](https://github.com/lsky-org/lsky-pro/commits/dev)
[![License](https://img.shields.io/badge/license-GPL_V3.0-yellowgreen.svg)](https://github.com/lsky-org/lsky-pro/blob/master/LICENSE)
[官网](https://www.lsky.pro) &middot;
[文档](https://docs.lsky.pro) &middot;
[社区](https://github.com/lsky-org/lsky-pro/discussions) &middot;
[演示](https://pic.vv1234.cn) &middot;
[Telegram 群组](https://t.me/lsky_pro)
[![License](https://img.shields.io/badge/license-GPL_V3.0-yellowgreen.svg)](https://github.com/wisp-x/lsky-pro/blob/master/LICENSE)
[![PHP](https://img.shields.io/badge/PHP->=5.6-orange.svg)](http://php.net)
[![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/wisp-x/lsky-pro.svg)](https://github.com/wisp-x/lsky-pro)
> [!WARNING]
> 开源版本已停止维护,不再进行新特性更新和 bug 修复。
> 下载稳定版请点击[这里](https://github.com/wisp-x/lsky-pro/releases),发现 bug 可发送邮件至邮箱i@wispx.cn或提交 [issues](https://github.com/wisp-x/lsky-pro/issues)
> 下载速度慢的可以移步 Coding https://dev.tencent.com/u/wispx/p/lsky-pro-releases/git
> master 分支为未安装三方拓展的版本,通常包含了最新未发布版本的一些实验性新特性和修复补丁,正式版本请点击 [这里](https://github.com/lsky-org/lsky-pro/releases) 下载。
> 发现 bug 请提交 [issues](https://github.com/lsky-org/lsky-pro/issues) (提问前建议阅读[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md))
> 有任何想法、建议、或分享,请移步 [社区](https://github.com/lsky-org/lsky-pro/discussions)
![homepage.png](./public/static/app/images/demo/1.png)
![homepage.png](./public/static/app/images/demo/2.png)
![看不见图片请使用科学上网](https://user-images.githubusercontent.com/22728201/157242302-bfbd04a0-fb30-4241-800e-cc2b1dad9b19.png)
![看不见图片请使用科学上网](https://user-images.githubusercontent.com/22728201/157242314-5716d578-fee5-4083-8d91-0d98cb2545d9.png)
主要特性
---
- 支持第三方云储存,支持本地、阿里云 OSS、腾讯云 COS、七牛云、又拍云。
- 支持多图上传、拖拽上传、上传预览、全屏预览、页面响应式布局。
- 简洁的图片管理功能,支持鼠标右键、单选多选等操作。
- 强大的图片预览功能,支持响应式。
- 支持全局配置用户初始剩余储存空间、支持单个设置用户剩余储存空间。
- 支持一键复制图片外链、二维码扫描链接。
- 支持设置上传文件、文件夹路径命名规则。
- 支持图片鉴黄功能。
- 支持文件夹分类功能。
- 对外开放的上传接口。
### 📌 TODO
* [x] 支持`本地`等多种第三方云储存 `AWS S3``阿里云 OSS``腾讯云 COS``七牛云``又拍云``SFTP``FTP``WebDav``Minio`
* [x] 多种数据库驱动支持,`MySQL 5.7+``PostgreSQL 9.6+``SQLite 3.8.8+``SQL Server 2017+`
* [x] 支持配置使用多种缓存驱动,`Memcached``Redis``DynamoDB`、等其他关系型数据库,默认以文件的方式缓存
* [x] 多图上传、拖拽上传、粘贴上传、动态设置策略上传、复制、一键复制链接
* [x] 强大的图片管理功能,瀑布流展示,支持鼠标右键、单选多选、重命名等操作
* [x] 自由度极高的角色组配置,可以为每个组配置多个储存策略,同时储存策略可以配置多个角色组
* [x] 可针对角色组设置上传文件、文件夹路径命名规则、上传频率限制、图片审核等功能
* [x] 支持图片水印、文字水印、水印平铺、设置水印位置、X/y 轴偏移量设置、旋转角度等
* [x] 支持通过接口上传、管理图片、管理相册
* [x] 支持在线增量更新、跨版本更新
* [x] 图片广场
安装
---
* PHP 版本 &ge; 5.6(建议使用PHP7+)
* mysql 版本 &ge; 5.5
* mysqli 支持
* fileinfo
* curl 拓
* rewrite
### 🛠 安装
- PHP >= 8.0.2
- BCMath PHP 扩展
- Ctype PHP 扩展
- DOM PHP 拓展
- Fileinfo PHP 扩
- JSON PHP 扩
- Mbstring PHP 扩展
- OpenSSL PHP 扩展
- PDO PHP 扩展
- Tokenizer PHP 扩展
- XML PHP 扩展
- Imagick 拓展
- exec、shell_exec 函数
- readlink、symlink 函数
- putenv、getenv 函数
- chmod、chown、fileperms 函数
安装教程
---
1. 下载兰空,上传至 web 运行环境,解压。
2. 设置运行目录为 public。
3. 配置 Rewrite 规则:
##### Nginx
```
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last; break;
}
}
```
### 😋 鸣谢
- [Laravel](https://laravel.com)
- [Tailwindcss](https://tailwindcss.com)
- [Fontawesome](https://fontawesome.com)
- [Echarts](https://echarts.apache.org)
- [Intervention/image](https://github.com/Intervention/image)
- [league/flysystem](https://flysystem.thephpleague.com)
- [overtrue](https://github.com/overtrue)
- [Jquery](https://jquery.com)
- [jQuery-File-Upload](https://github.com/blueimp/jQuery-File-Upload)
- [Alpinejs](https://alpinejs.dev/)
- [Viewer.js](https://github.com/fengyuanchen/viewerjs)
- [DragSelect](https://github.com/ThibaultJanBeyer/DragSelect)
- [Justified-Gallery](https://github.com/miromannino/Justified-Gallery)
- [Clipboard.js](https://github.com/zenorocha/clipboard.js)
##### Apache:
Apache 直接使用 .htaccess 即可
### 💰 捐赠
Lsky Pro 的开发和更新等,都是作者在业余时间独立开发,并免费开源使用,如果您认可我的作品,并且觉得对你有所帮助我愿意接受来自各方面的捐赠😃。
<table width="100%">
<tr>
<th>支付宝</th>
<th>微信</th>
</tr>
<tr>
<td><img alt="看不见图片请使用科学上网" src="https://raw.githubusercontent.com/lsky-org/lsky-pro/82988ebe2edd32264d609b26bf9132b3dce7c39e/public/static/app/images/demo/alipay.png"></td>
<td><img alt="看不见图片请使用科学上网" src="https://raw.githubusercontent.com/lsky-org/lsky-pro/82988ebe2edd32264d609b26bf9132b3dce7c39e/public/static/app/images/demo/wechat.jpeg"></td>
</tr>
</table>
4. 访问首页,未安装自动跳转至安装页面,根据页面提示安装即可。
5. 安装完成以后请设置 runtime 目录0755权限如果你使用本地存储public 目录也需要设置为0755权限
### 🤩 Stargazers over time
[![Stargazers over time](https://starchart.cc/lsky-org/lsky-pro.svg)](https://starchart.cc/lsky-org/lsky-pro)
如何更新到最新版?
---
升级过程并不复杂,但也简单粗暴,总共分为四个步骤。
1. [下载](https://github.com/wisp-x/lsky-pro/releases)最新版程序并解压到本地。
2. 将旧版程序 ```config/db.php``` 文件复制到新版程序的 ```config``` 文件夹,如果你使用的是本地储存策略,文件是储存在本地的,请注意也要将你旧版本的图片资源移动到新程序对应的目录。
3. 删除旧版本程序所有文件,上传最新版程序到站点根目录即可(这一步也可以直接覆盖,但会有残留文件,不选择覆盖的话建议先打包备份旧版本)。
4. 使用管理员账号登录,访问任意页面会跳转到升级数据库结构页面,根据提示操作即可。
FAQ如果覆盖更新文件后无法重定向到更新页面或首页出现错误请直接访问 ```http://域名/install/update.html``` 进行更新。
如何修改网站运行目录?
---
默认程序的入口文件在 ```public``` 目录下,所以需要把 public 目录设置为网站运行目录,这样做是为了:
> 入口文件位置的设计是为了让应用部署更安全,```public``` 目录为web可访问目录其他的文件都可以放到非WEB访问目录下面。
而你如果安装时没有设置,使用 ```http://域名/public``` 的方式来访问站点,会导致 css 和 js 等静态资源无法获取。
如果你 无法 或 不会 设置运行目录,可以将 public 目录下的所有文件和文件夹(包括 .htaccess 文件)移动到根目录
(和 ```application``` 文件夹同级)即可,尽管我们不推荐你这么做,这样会导致应用程序核心文件暴露在外。
移动文件以后,打开根目录 ```index.php``` 文件,修改如下:
```php
<?php
// [ 应用入口文件 ]
namespace think;
define('ROOT_PATH', __DIR__ . DIRECTORY_SEPARATOR);
// 加载基础文件
require __DIR__ . '/thinkphp/base.php';
// 执行应用并响应
Container::get('app')->bind('index')->run()->send();
```
注意:请不要使用记事本打开修改,修改完成后保存即可。
联系我
---
- QQ1591788658
### 📧 联系我
- Email: i@wispx.cn
- Blog[https://www.wispx.cn](https://www.wispx.cn)
鸣谢
---
- ThinkPHP
- Jquery
- BootStrap
- Mdui
- viewer.js
- context.js
开源许可
---
### 📃 开源许可
[GPL 3.0](https://opensource.org/licenses/GPL-3.0)
Copyright (c) 2018-present Lsky Pro.

View File

@@ -0,0 +1,113 @@
<?php
namespace App\Console\Commands;
use App\Utils;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Config;
class Install extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lsky:install';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Install Lsky Pro.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
$this->signature = implode(' ', [
'lsky:install',
'{--connection=mysql : Database type}',
'{--host=127.0.0.1 : Database connection address}',
'{--port=3306 : Database connection port}',
'{--database= : Database name}',
'{--username=root : Database connection user name}',
'{--password=root : Database connection password}',
]);
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
// 判断是否已经安装
if (file_exists(base_path('installed.lock'))) {
$this->warn('Already installed. if you want to reinstall, please remove installed.lock file.');
return 0;
}
$driver = $this->option('connection');
$connection = "database.connections.{$driver}";
$options = [
'connection' => $this->option('connection'),
'host' => $this->option('host'),
'port' => $this->option('port'),
'database' => $this->option('database'),
'username' => $this->option('username'),
'password' => $this->option('password'),
];
$configs = array_intersect_key($options, config($connection));
// 覆盖默认配置
Config::set($connection, array_merge(config($connection), $configs));
// 设置默认数据库驱动
Config::set('database.default', $driver);
clearstatcache(true);
try {
if ($options['connection'] === 'sqlite' && ! $options['database']) {
$options['database'] = database_path('database.sqlite');
file_put_contents($options['database'], '');
Config::set('database.connections.sqlite.database', $options['database']);
}
// 执行数据库迁移
Artisan::call('migrate:fresh', ['--force' => true], outputBuffer: $this->output);
// 填充数据
Artisan::call('db:seed', ['--force' => true, '--class' => 'InstallSeeder'], outputBuffer: $this->output);
// 更新 env 文件
$replaces = collect($options)->transform(fn ($item, $key) => ['DB_'.strtoupper($key) => $item])->collapse();
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
$replaces->map(fn ($item, $key) => $this->replacementPattern($key, env($key, '')))->values()->toArray(),
$replaces->map(fn ($item, $key) => "{$key}={$item}")->values()->toArray(),
file_get_contents($this->laravel->environmentFilePath())
));
// 创建锁文件
file_put_contents(base_path('installed.lock'), '');
} catch (\Throwable $e) {
$this->warn("Installation error!\n");
$this->error($e->getMessage());
Utils::e($e, '执行数据库安装程序时出现异常');
return 1;
}
$this->info('Install success!');
return 0;
}
protected function replacementPattern(string $name, string $value): string
{
$escaped = preg_quote('='.$value, '/');
return "/^{$name}{$escaped}/m";
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Console\Commands;
use App\Models\Image;
use App\Services\ImageService;
use Illuminate\Console\Command;
use League\Flysystem\FilesystemException;
use Symfony\Component\Console\Helper\ProgressBar;
class MakeThumbnails extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lsky:thumbnails';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Make images thumbnails.';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$progress = new ProgressBar($this->output, Image::query()->count());
$progress->setMessage('获取图片处理中...');
$progress->start();
$service = new ImageService();
/** @var Image $image */
foreach (Image::query()->whereNotNull('strategy_id')->cursor() as $image) {
try {
$service->makeThumbnail(
image: $image,
data: $image->filesystem()->read($image->pathname),
force: true,
);
$progress->advance();
} catch (\Throwable $e) {
$this->error("缩略图生成失败, {$e->getMessage()}");
}
}
$progress->finish();
return 0;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Console\Commands;
use App\Enums\ConfigKey;
use App\Services\UpgradeService;
use App\Utils;
use Illuminate\Console\Command;
class Upgrade extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lsky:upgrade';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Upgrade Lsky Pro.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
return (new UpgradeService(Utils::config(ConfigKey::AppVersion)))->upgrade() ? 0 : 1;
}
}

32
app/Console/Kernel.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

48
app/Enums/ConfigKey.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
namespace App\Enums;
final class ConfigKey
{
/** @var string 是否启用注册 */
const IsEnableRegistration = 'is_enable_registration';
/** @var string 是否启用画廊 */
const IsEnableGallery = 'is_enable_gallery';
/** @var string 是否启用接口 */
const IsEnableApi = 'is_enable_api';
/** @var string 程序名称 */
const AppName = 'app_name';
/** @var string 程序版本 */
const AppVersion = 'app_version';
/** @var string 站点关键字 */
const SiteKeywords = 'site_keywords';
/** @var string 站点描述 */
const SiteDescription = 'site_description';
/** @var string 站点公告 */
const SiteNotice = 'site_notice';
/** @var string icp备案号 */
const IcpNo = 'icp_no';
/** @var string 是否允许游客上传 */
const IsAllowGuestUpload = 'is_allow_guest_upload';
/** @var string 用户初始容量(kb) */
const UserInitialCapacity = 'user_initial_capacity';
/** @var string 账户是否需要验证 */
const IsUserNeedVerify = 'is_user_need_verify';
/** @var string 邮件配置 */
const Mail = 'mail';
/** @var string 角色组默认配置 */
const Group = 'group';
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Enums;
final class GroupConfigKey
{
/** @var string 最大文件大小 */
const MaximumFileSize = 'maximum_file_size';
/** @var string 并发上传数量 */
const ConcurrentUploadNum = 'concurrent_upload_num';
/** @var string 上传是否需要审查 */
const IsEnableScan = 'is_enable_scan';
/** @var string 审查配置 */
const ScanConfigs = 'scan_configs';
/** @var string 违规图片审查后动作 */
const ScannedAction = 'scanned_action';
/** @var string 是否启用原图保护功能 */
const IsEnableOriginalProtection = 'is_enable_original_protection';
/** @var string 上传是否启用水印功能 */
const IsEnableWatermark = 'is_enable_watermark';
/** @var string 水印配置 */
const WatermarkConfigs = 'watermark_configs';
/** @var string 每分钟上传限制 */
const LimitPerMinute = 'limit_per_minute';
/** @var string 每小时上传限制 */
const LimitPerHour = 'limit_per_hour';
/** @var string 每天上传数量限制 */
const LimitPerDay = 'limit_per_day';
/** @var string 每周上传数量限制 */
const LimitPerWeek = 'limit_per_week';
/** @var string 每月上传数量限制 */
const LimitPerMonth = 'limit_per_month';
/** @var string 允许上传的文件后缀 */
const AcceptedFileSuffixes = 'accepted_file_suffixes';
/** @var string 路径命名规则 */
const PathNamingRule = 'path_naming_rule';
/** @var string 文件命名规则 */
const FileNamingRule = 'file_naming_rule';
/** @var string 图片缓存时间 */
const ImageCacheTtl = 'image_cache_ttl';
/** @var string 图片保存格式 */
const ImageSaveFormat = 'image_save_format';
/** @var string 图片保存质量 */
const ImageSaveQuality = 'image_save_quality';
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Enums;
final class ImagePermission
{
const Public = 1; // 公开
const Private = 0; // 私有
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Enums\Mail;
final class SmtpOption
{
const Transport = 'smtp';
/** @var string 主机地址 */
const Host = 'host';
/** @var string 连接端口 */
const Port = 'port';
/** @var string 加密方式 */
const Encryption = 'encryption';
/** @var string 用户名 */
const Username = 'username';
/** @var string 密码 */
const Password = 'password';
/** @var string 超时时间 */
const Timeout = 'timeout';
/** @var string 发件人地址 */
const FromAddress = 'from_address';
/** @var string 发件人名称 */
const FromName = 'from_name';
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Enums;
final class PastedAction
{
const Upload = 2; // 直接上传
const Waiting = 1; // 等待上传
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Enums\Scan;
final class AliyunOption
{
/** @var string AccessKeyId */
const AccessKeyId = 'access_key_id';
/** @var string AccessKeySecret */
const AccessKeySecret = 'access_key_secret';
/** @var string RegionId */
const RegionId = 'region_id';
/** @var string 场景 */
const Scenes = 'scenes';
/** @var string 业务场景 */
const BizType = 'biz_type';
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Enums\Scan;
final class NsfwJsOption
{
/** @var string 接口地址 */
const ApiUrl = 'api_url';
/** @var string 表单属性名称 */
const AttrName = 'attr_name';
/** @var string 阈值 */
const Threshold = 'threshold';
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Enums\Scan;
final class TencentOption
{
/** @var string SecretId */
const SecretId = 'secret_id';
/** @var string SecretKey */
const SecretKey = 'secret_key';
/** @var string Region */
const Region = 'region';
/** @var string Endpoint */
const Endpoint = 'endpoint';
/** @var string 业务场景 */
const BizType = 'biz_type';
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Enums\Strategy;
final class CosOption
{
/** @var string 访问地址 */
const Url = 'url';
/** @var string AppId */
const AppId = 'app_id';
/** @var string SecretId */
const SecretId = 'secret_id';
/** @var string SecretKey */
const SecretKey = 'secret_key';
/** @var string 所属地域 */
const Region = 'region';
/** @var string 储存桶名称 */
const Bucket = 'bucket';
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Enums\Strategy;
final class FtpOption
{
/** @var string 访问url */
const Url = 'url';
/** @var string 根目录 */
const Root = 'root';
/** @var string 主机地址 */
const Host = 'host';
/** @var string 连接名称 */
const Username = 'username';
/** @var string 密码 */
const Password = 'password';
/** @var string 连接端口 */
const Port = 'port';
/** @var string 加密连接 */
const Ssl = 'ssl';
/** @var string 被动模式 */
const Passive = 'passive';
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Enums\Strategy;
final class KodoOption
{
/** @var string 访问地址 */
const Url = 'url';
/** @var string AccessKey */
const AccessKey = 'access_key';
/** @var string SecretKey */
const SecretKey = 'secret_key';
/** @var string Bucket */
const Bucket = 'bucket';
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Enums\Strategy;
final class LocalOption
{
/** @var string 访问url */
const Url = 'url';
/** @var string 根目录 */
const Root = 'root';
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Enums\Strategy;
final class MinioOption
{
/** @var string 访问url */
const Url = 'url';
/** @var string AccessKey */
const AccessKey = 'access_key';
/** @var string SecretKey */
const SecretKey = 'secret_key';
/** @var string Endpoint */
const Endpoint = 'endpoint';
/** @var string 区域 */
const Region = 'region';
/** @var string Bucket */
const Bucket = 'bucket';
/** @var string BucketEndpoint */
const BucketEndpoint = 'bucket_endpoint';
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Enums\Strategy;
final class OssOption
{
/** @var string 访问地址 */
const Url = 'url';
/** @var string AccessKeyId */
const AccessKeyId = 'access_key_id';
/** @var string AccessKeySecret */
const AccessKeySecret = 'access_key_secret';
/** @var string Endpoint */
const Endpoint = 'endpoint';
/** @var string 储存桶名称 */
const Bucket = 'bucket';
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Enums\Strategy;
final class S3Option
{
/** @var string 访问url */
const Url = 'url';
/** @var string AccessKeyId */
const AccessKeyId = 'access_key_id';
/** @var string SecretAccessKey */
const SecretAccessKey = 'secret_access_key';
/** @var string Endpoint */
const Endpoint = 'endpoint';
/** @var string 区域 */
const Region = 'region';
/** @var string Bucket */
const Bucket = 'bucket';
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Enums\Strategy;
final class SftpOption
{
/** @var string 访问url */
const Url = 'url';
/** @var string 根目录 */
const Root = 'root';
/** @var string 主机地址 */
const Host = 'host';
/** @var string 连接名称 */
const Username = 'username';
/** @var string 密码 */
const Password = 'password';
/** @var string 私钥 */
const PrivateKey = 'private_key';
/** @var string 密钥口令 */
const Passphrase = 'passphrase';
/** @var string 连接端口 */
const Port = 'port';
/** @var string 使用代理 */
const UseAgent = 'use_agent';
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Enums\Strategy;
final class UssOption
{
/** @var string 访问地址 */
const Url = 'url';
/** @var string 操作员 */
const Operator = 'operator';
/** @var string 密码 */
const Password = 'password';
/** @var string 服务名称 */
const Service = 'service';
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Enums\Strategy;
final class WebDavOption
{
/** @var string 访问url */
const Url = 'url';
/** @var string baseUri */
const BaseUri = 'base_uri';
/** @var string 用户名 */
const Username = 'username';
/** @var string 密码 */
const Password = 'password';
/** @var string 认证方式 */
const AuthType = 'auth_type';
/** @var string 地址前缀 */
const Prefix = 'prefix';
}

36
app/Enums/StrategyKey.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
namespace App\Enums;
final class StrategyKey
{
/** @var int 本地 */
const Local = 1;
/** @var int S3 */
const S3 = 2;
/** @var int 阿里云 Oss */
const Oss = 3;
/** @var int 腾讯云 Cos */
const Cos = 4;
/** @var int 七牛云 Kodo */
const Kodo = 5;
/** @var int 又拍云 Uss */
const Uss = 6;
/** @var int Sftp */
const Sftp = 7;
/** @var int Ftp */
const Ftp = 8;
/** @var int WebDav */
const Webdav = 9;
/** @var int Minio */
const Minio = 10;
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Enums;
final class UserConfigKey
{
/** @var string 默认相册 */
const DefaultAlbum = 'default_album';
/** @var string 默认策略 */
const DefaultStrategy = 'default_strategy';
/** @var string 默认权限 */
const DefaultPermission = 'default_permission';
/** @var string 图片粘贴后动作 */
const PastedAction = 'pasted_action';
/** @var string 上传是否自动清除预览 */
const IsAutoClearPreview = 'is_auto_clear_preview';
}

12
app/Enums/UserStatus.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
namespace App\Enums;
final class UserStatus
{
/** @var int 正常 */
const Normal = 1;
/** @var int 冻结 */
const Frozen = 0;
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Enums\Watermark;
final class FontOption
{
/** @var string 文本内容 */
const Text = 'text';
/** @var string 文字颜色 */
const Color = 'color';
/** @var string 字体大小 */
const Size = 'size';
/** @var string 字体文件路径 */
const Font = 'font';
/** @var string 水印位置 */
const Position = 'position';
/** @var string 旋转角度 */
const Angle = 'angle';
/** @var string X轴偏移量 */
const X = 'x';
/** @var string Y轴偏移量 */
const Y = 'y';
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Enums\Watermark;
final class ImageOption
{
/** @var string 水印图片 */
const Image = 'image';
/** @var string 水印位置 */
const Position = 'position';
/** @var string 水印图片宽 */
const Width = 'width';
/** @var string 水印图片高 */
const Height = 'height';
/** @var string 旋转角度 */
const Rotate = 'rotate';
/** @var string 不透明度 */
const Opacity = 'opacity';
/** @var string X轴偏移量 */
const X = 'x';
/** @var string Y轴偏移量 */
const Y = 'y';
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Enums\Watermark;
final class Mode
{
const Overlay = 1; // 覆盖原图
const Dynamic = 2; // 动态生成
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Exceptions;
use App\Http\Result;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Throwable;
class Handler extends ExceptionHandler
{
use Result;
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
$this->renderable(function (ThrottleRequestsException $e) {
return $this->fail($e->getMessage())->setStatusCode(429);
});
}
protected function unauthenticated($request, AuthenticationException $exception)
{
return $this->shouldReturnJson($request, $exception)
? $this->fail($exception->getMessage())->setStatusCode(401)
: redirect()->guest($exception->redirectTo() ?? route('login'));
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Exceptions;
use Exception;
class UploadException extends Exception
{
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Image;
use App\Models\User;
use App\Utils;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\View\View;
class ConsoleController extends Controller
{
public function index(): View
{
$carbon = Carbon::now();
$format = 'Y-m-d H:i:s';
$numbers = [
'today' => Image::query()->whereBetween('created_at', [$carbon->startOfDay()->format($format), $carbon->endOfDay()->format($format)])->count(),
'yesterday' => Image::query()->whereBetween('created_at', [$carbon->yesterday()->startOfDay()->format($format), $carbon->yesterday()->endOfDay()->format($format)])->count(),
'week' => Image::query()->whereBetween('created_at', [$carbon->startOfWeek()->format($format), $carbon->endOfWeek()->format($format)])->count(),
'month' => Image::query()->whereBetween('created_at', [$carbon->startOfMonth()->format($format), $carbon->endOfMonth()->format($format)])->count(),
];
$start = Carbon::now()->parse('-30 day')->startOfDay();
$end = Carbon::now()->endOfDay();
$dates = Utils::makeDateRange($start->format('Y-m-d'), $end->format('Y-m-d'));
$fields = ['游客上传', '用户上传', '新用户'];
$images = Image::query()
->whereBetween('created_at', [$start->format($format), $end->format($format)])
->get(['user_id', 'created_at'])
->transform(function (Image $image) {
$image['date'] = $image->created_at->format('Y-m-d');
return $image;
})->groupBy('date');
$users = User::query()
->whereBetween('created_at', [$start->format($format), $end->format($format)])
->get()
->transform(function (User $user) {
$user['date'] = $user->created_at->format('Y-m-d');
return $user;
})->groupBy('date');
$data = collect(array_map(fn() => 0, array_flip($dates)));
$array = [
$data->merge($images->map(fn(Collection $items) => $items->whereNull('user_id')->count())),
$data->merge($images->map(fn(Collection $items) => $items->whereNotNull('user_id')->count())),
$data->merge($users->map(fn(Collection $items) => $items->count())),
];
$datasets = collect($fields)->transform(function ($item, $index) use ($dates, $array) {
return [
'name' => $item,
'type' => 'line',
'data' => $array[$index]->values(),
];
});
return view('admin.console.index', compact('fields', 'numbers', 'dates', 'datasets'));
}
}

View File

@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\GroupRequest;
use App\Models\Group;
use App\Models\Image;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class GroupController extends Controller
{
private function share()
{
\Illuminate\Support\Facades\View::share([
'default' => Group::getDefaultConfigs(),
'positions' => Group::POSITIONS,
'scenes' => Group::SCENES,
]);
}
public function index(Request $request): View
{
$keywords = $request->query('keywords');
$groups = Group::query()->when($keywords, function (Builder $builder, $keywords) {
$builder->where('name', 'like', "%{$keywords}%");
})->withCount('users')->withCount('strategies')->latest()->paginate();
$groups->appends(compact('keywords'));
$this->share();
return view('admin.group.index', compact('groups'));
}
public function add(): View
{
$this->share();
return view('admin.group.add');
}
public function edit(Request $request): View
{
$group = Group::query()->findOrFail($request->route('id'));
$this->share();
return view('admin.group.edit', compact('group'));
}
public function create(GroupRequest $request): Response
{
DB::transaction(function () use ($request) {
$group = new Group();
$group->fill($request->validated());
$group->save();
});
$this->share();
return $this->success('创建成功');
}
public function update(GroupRequest $request): Response
{
DB::beginTransaction();
try {
/** @var Group $group */
$group = Group::query()->findOrFail($request->route('id'));
$group->fill($request->validated());
if ($group->isDirty('is_default') && ! $group->is_default) {
if (! Group::query()->where('is_default', true)->where('id', '<>', $group->id)->exists()) {
return $this->fail('系统至少需要保留一个默认组');
}
}
if ($group->isDirty('is_guest') && ! $group->is_guest) {
if (! Group::query()->where('is_guest', true)->where('id', '<>', $group->id)->exists()) {
return $this->fail('系统至少需要保留一个游客组');
}
}
$group->save();
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
return $this->fail('保存失败');
}
return $this->success('保存成功');
}
public function delete(Request $request): Response
{
/** @var Group $group */
if ($group = Group::query()->find($request->route('id'))) {
if ($group->is_default || $group->is_guest) {
return $this->fail('默认组和游客组无法删除');
}
DB::transaction(function () use ($group) {
$group->users()->update(['group_id' => Group::query()->where('is_default', true)->value('id')]);
$group->delete();
});
}
return $this->success('删除成功');
}
public function clearCache(Request $request): Response
{
/** @var Group $group */
if ($group = Group::query()->find($request->route('id'))) {
/** @var Image $image */
foreach ($group->images()->select('key')->cursor() as $image) {
Cache::forget('image_'.$image->key);
}
}
return $this->success('清除成功');
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Enums\ImagePermission;
use App\Http\Controllers\Controller;
use App\Models\Image;
use App\Services\UserService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Str;
use Illuminate\View\View;
class ImageController extends Controller
{
public function index(Request $request): View
{
$keywords = $request->query('keywords');
$images = Image::query()->with(['user' => function (BelongsTo $belongsTo) {
$belongsTo->withSum('images', 'size');
}, 'album', 'group', 'strategy'])->when($keywords, function (Builder $builder, $keywords) {
$words = [];
$qualifiers = [
'name:', 'album:', 'group:', 'strategy:', 'email:', 'extension:', 'md5:', 'sha1:', 'ip:', 'is:', 'order:',
];
collect(array_filter(explode(' ', $keywords)))->filter(function ($keyword) use ($qualifiers, &$words) {
if (Str::startsWith($keyword, $qualifiers)) {
return true;
}
$words[] = $keyword;
return false;
})->each(function ($filter) use ($builder) {
match ($filter) {
'is:public' => $builder->where('permission', ImagePermission::Public),
'is:private' => $builder->where('permission', ImagePermission::Private),
'is:unhealthy' => $builder->where('is_unhealthy', 1),
'is:guest' => $builder->whereNull('user_id'),
'is:adminer' => $builder->whereHas('user', fn (Builder $builder) => $builder->where('is_adminer', 1)),
'order:earliest' => $builder->orderBy('created_at'),
'order:utmost' => $builder->orderByDesc('size'),
'order:least' => $builder->orderBy('size'),
default => 0,
};
[$qualifier, $value] = explode(':', $filter);
if ($value) {
$callback = fn (Builder $builder) => $builder->where('name', $value);
match ($qualifier) {
'name' => $builder->whereHas('user', $callback),
'album' => $builder->whereHas('album', $callback),
'group' => $builder->whereHas('group', $callback),
'strategy' => $builder->whereHas('strategy', $callback),
'email' => $builder->whereHas('user', fn (Builder $builder) => $builder->where('email', $value)),
'extension' => $builder->where('extension', $value),
'md5' => $builder->where('md5', $value),
'sha1' => $builder->where('sha1', $value),
'ip' => $builder->where('ip', $value),
default => 0
};
}
});
foreach ($words as $word) {
$builder->where('name', 'like', "%{$word}%")
->orWhere('origin_name', 'like', "%{$word}%")
->orWhere('alias_name', 'like', "%{$word}%");
}
})->latest()->paginate(40);
$images->getCollection()->each(function (Image $image) {
$image->append('url', 'pathname', 'thumb_url');
$image->album?->setVisible(['name']);
$image->group?->setVisible(['name']);
$image->strategy?->setVisible(['name']);
});
$images->appends(compact('keywords'));
return view('admin.image.index', compact('images'));
}
public function update(): Response
{
return $this->success();
}
public function delete(Request $request): Response
{
/** @var Image $image */
$image = Image::with('user', 'strategy', 'album')->find($request->route('id'));
(new UserService())->deleteImages([$image->id]);
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Enums\ConfigKey;
use App\Http\Controllers\Controller;
use App\Mail\Test;
use App\Models\Config;
use App\Services\UpgradeService;
use App\Utils;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
use Illuminate\View\View;
class SettingController extends Controller
{
public function index(): View
{
$configs = Utils::config();
return view('admin.setting.index', compact('configs'));
}
public function save(Request $request): Response
{
foreach ($request->all() as $key => $value) {
Config::query()->where('name', $key)->update(['value' => $value]);
}
Cache::flush();
return $this->success('保存成功');
}
public function mailTest(Request $request): Response
{
try {
Mail::to($request->post('email'))->send(new Test());
} catch (\Throwable $e) {
return $this->fail($e->getMessage());
}
return $this->success('发送成功');
}
public function checkUpdate(): Response
{
$version = Utils::config(ConfigKey::AppVersion);
$service = new UpgradeService($version);
try {
$data = [
'is_update' => $service->check(),
];
if ($data['is_update']) {
$data['version'] = $service->getVersions()->first();
}
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
return $this->success('success', $data);
}
public function upgrade()
{
ignore_user_abort(true);
set_time_limit(0);
$version = Utils::config(ConfigKey::AppVersion);
$service = new UpgradeService($version);
$this->success()->send();
$service->upgrade();
flush();
}
public function upgradeProgress(): Response
{
return $this->success('success', Cache::get('upgrade_progress'));
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\StrategyRequest;
use App\Models\Strategy;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class StrategyController extends Controller
{
public function index(Request $request): View
{
$keywords = $request->query('keywords');
$strategies = Strategy::query()->when($keywords, function (Builder $builder, $keywords) {
$builder->where('name', 'like', "%{$keywords}%")->orWhere('intro', 'like', "%{$keywords}%");
})->withCount('images')->withSum('images', 'size')->latest()->paginate();
$strategies->appends(compact('keywords'));
return view('admin.strategy.index', compact('strategies'));
}
public function add(): View
{
return view('admin.strategy.add');
}
public function edit(Request $request): View
{
/** @var Strategy $strategy */
$strategy = Strategy::query()->findOrFail($request->route('id'));
return view('admin.strategy.edit', compact('strategy'));
}
public function create(StrategyRequest $request): Response
{
$validated = $request->validated();
$strategy = new Strategy($validated);
DB::transaction(function () use ($strategy, $validated) {
$strategy->save();
$strategy->groups()->attach($validated['groups'] ?? []);
});
return $this->success('创建成功');
}
public function update(StrategyRequest $request): Response
{
$validated = $request->validated();
/** @var Strategy $strategy */
$strategy = Strategy::query()->findOrFail($request->route('id'));
$strategy->fill($request->validated());
DB::transaction(function () use ($strategy, $validated) {
$strategy->save();
$strategy->groups()->sync($validated['groups'] ?? []);
});
return $this->success('保存成功');
}
public function delete(Request $request): Response
{
/** @var Strategy $strategy */
if ($strategy = Strategy::query()->find($request->route('id'))) {
DB::transaction(function () use ($strategy) {
$strategy->images()->update(['strategy_id' => null]);
$strategy->delete();
});
}
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\UserRequest;
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\View;
class UserController extends Controller
{
public function index(Request $request): View
{
$status = $request->query('status');
$keywords = $request->query('keywords');
$users = User::query()->when($status > -1, function (Builder $builder) use ($status) {
$builder->where('status', $status);
})->when($keywords, function (Builder $builder, $keywords) {
$builder->where('name', 'like', "%{$keywords}%")->orWhere('email', 'like', "%{$keywords}%");
})->with('group')->withSum('images', 'size')->latest()->paginate();
$users->getCollection()->each(function (User $user) {
$user->group->setVisible(['name']);
});
$statuses = [-1 => '全部', 1 => '正常', 0 => '冻结'];
$users->appends(compact('status', 'keywords'));
return view('admin.user.index', compact('users', 'statuses'));
}
public function edit(Request $request): View
{
$user = User::query()->findOrFail($request->route('id'));
return view('admin.user.edit', compact('user'));
}
public function update(UserRequest $request): Response
{
/** @var User $user */
$user = User::query()->findOrFail($request->route('id'));
$validated = $request->validated();
if (! empty($validated['password'])) {
$user->forceFill([
'password' => Hash::make($validated['password']),
'remember_token' => Str::random(60),
]);
event(new PasswordReset($user));
}
unset($validated['password']);
$user->fill($validated);
$user->group_id = $validated['group_id'];
if (!$user->save()) {
return $this->fail('保存失败');
}
return $this->success('保存成功');
}
public function delete(Request $request): Response
{
/** @var User $user */
if ($user = User::query()->find($request->route('id'))) {
DB::transaction(function () use ($user) {
$user->images()->update(['user_id' => null]);
$user->albums()->delete();
$user->delete();
});
}
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Models\Album;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class AlbumController extends Controller
{
public function index(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
$albums = $user->albums()->filter($request)->paginate(40);
$albums->getCollection()->each(function (Album $album) {
$album->setVisible(['id', 'name', 'intro', 'image_num']);
});
return $this->success('success', $albums);
}
public function destroy(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
$user->albums()->where('id', $request->route('id'))->delete();
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Exceptions\UploadException;
use App\Http\Controllers\Controller;
use App\Models\Image;
use App\Models\User;
use App\Services\ImageService;
use App\Services\UserService;
use App\Utils;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class ImageController extends Controller
{
/**
* @throws AuthenticationException
*/
public function upload(Request $request, ImageService $service): Response
{
if ($request->hasHeader('Authorization')) {
$guards = array_keys(config('auth.guards'));
if (empty($guards)) {
$guards = [null];
}
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
Auth::shouldUse($guard);
break;
}
}
if (! Auth::check()) {
throw new AuthenticationException('Authentication failed.');
}
}
try {
$image = $service->store($request);
} catch (UploadException $e) {
return $this->fail($e->getMessage());
} catch (\Throwable $e) {
Utils::e($e, 'Api 上传文件时发生异常');
if (config('app.debug')) {
return $this->fail($e->getMessage());
}
return $this->fail('服务异常,请稍后再试');
}
return $this->success('上传成功', $image->setAppends(['pathname', 'links'])->only(
'key', 'name', 'pathname', 'origin_name', 'size', 'mimetype', 'extension', 'md5', 'sha1', 'links'
));
}
public function images(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
$images = $user->images()->filter($request)->paginate(40)->withQueryString();
$images->getCollection()->each(function (Image $image) {
$image->human_date = $image->created_at->diffForHumans();
$image->date = $image->created_at->format('Y-m-d H:i:s');
$image->append(['pathname', 'links'])->setVisible([
'album', 'key', 'name', 'pathname', 'origin_name', 'size', 'mimetype', 'extension', 'md5', 'sha1',
'width', 'height', 'links', 'human_date', 'date',
]);
});
return $this->success('success', $images);
}
public function destroy(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
(new UserService())->deleteImages([$request->route('key')], $user, 'key');
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Models\Group;
use App\Models\Strategy;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class StrategyController extends Controller
{
public function index(): Response
{
/** @var Group $group */
$group = Auth::check() ? Auth::user()->group : Group::query()->where('is_guest', true)->first();
$strategies = $group->strategies()->get()->each(fn (Strategy $strategy) => $strategy->setVisible(['id', 'name']));
return $this->success('success', compact('strategies'));
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class TokenController extends Controller
{
public function store(Request $request): Response
{
try {
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
} catch (ValidationException $e) {
return $this->fail($e->validator->errors()->first());
}
/** @var User|null $user */
$user = User::query()->where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
return $this->fail('The email address or password is incorrect.');
}
$token = $user->createToken($user->email)->plainTextToken;
return $this->success('success', compact('token'));
}
public function clear(): Response
{
/** @var User $user */
$user = Auth::user();
$user->tokens()->delete();
return $this->success();
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{
public function index(): Response
{
/** @var User $user */
$user = Auth::user();
$user->used_capacity = $user->images()->sum('size') + 0;
$user->setVisible([
'name', 'avatar', 'email', 'capacity', 'used_capacity', 'url', 'image_num', 'album_num', 'registered_ip'
]);
return $this->success('success', $user);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.login');
}
/**
* Handle an incoming authentication request.
*
* @param \App\Http\Requests\Auth\LoginRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);
}
/**
* Destroy an authenticated session.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Request $request)
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
class ConfirmablePasswordController extends Controller
{
/**
* Show the confirm password view.
*
* @return \Illuminate\View\View
*/
public function show()
{
return view('auth.confirm-password');
}
/**
* Confirm the user's password.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function store(Request $request)
{
if (! Auth::guard('web')->validate([
'email' => $request->user()->email,
'password' => $request->password,
])) {
throw ValidationException::withMessages([
'password' => __('auth.password'),
]);
}
$request->session()->put('auth.password_confirmed_at', time());
return redirect()->intended(RouteServiceProvider::HOME);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
class EmailVerificationNotificationController extends Controller
{
/**
* Send a new email verification notification.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME);
}
$request->user()->sendEmailVerificationNotification();
return back()->with('status', 'verification-link-sent');
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
class EmailVerificationPromptController extends Controller
{
/**
* Display the email verification prompt.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function __invoke(Request $request)
{
return $request->user()->hasVerifiedEmail()
? redirect()->intended(RouteServiceProvider::HOME)
: view('auth.verify-email');
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
class NewPasswordController extends Controller
{
/**
* Display the password reset view.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
return view('auth.reset-password', ['request' => $request]);
}
/**
* Handle an incoming new password request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user) use ($request) {
$user->forceFill([
'password' => Hash::make($request->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $status == Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class PasswordResetLinkController extends Controller
{
/**
* Display the password reset link request view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.forgot-password');
}
/**
* Handle an incoming password reset link request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
]);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$status = Password::sendResetLink(
$request->only('email')
);
return $status == Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Enums\ConfigKey;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use App\Utils;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
class RegisteredUserController extends Controller
{
/**
* Display the registration view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.register');
}
/**
* Handle an incoming registration request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'registered_ip' => $request->ip(),
]);
if (Utils::config(ConfigKey::IsUserNeedVerify)) {
event(new Registered($user));
}
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*
* @param \Illuminate\Foundation\Auth\EmailVerificationRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function __invoke(EmailVerificationRequest $request)
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Http\Controllers\Common;
use App\Http\Controllers\Controller;
use Illuminate\View\View;
class ApiController extends Controller
{
public function index(): View
{
return view('common.api');
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers\Common;
use App\Enums\ImagePermission;
use App\Http\Controllers\Controller;
use App\Models\Image;
use Illuminate\View\View;
class GalleryController extends Controller
{
public function index(): View
{
$images = Image::query()
->with('user')
->whereNotNull('user_id')
->where('is_unhealthy', false)
->where('permission', ImagePermission::Public)
->latest()
->simplePaginate(40);
return view('common.gallery', compact('images'));
}
}

View File

@@ -0,0 +1,205 @@
<?php
namespace App\Http\Controllers;
use App\Enums\GroupConfigKey;
use App\Enums\UserStatus;
use App\Enums\Watermark\Mode;
use App\Exceptions\UploadException;
use App\Http\Result;
use App\Models\Group;
use App\Models\Image;
use App\Models\Strategy;
use App\Models\User;
use App\Services\ImageService;
use App\Utils;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;
use Illuminate\View\View;
use Intervention\Image\Facades\Image as InterventionImage;
use League\Flysystem\FilesystemException;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\StreamedResponse;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, Result;
public function install(Request $request): View|Response
{
if (file_exists(base_path('installed.lock'))) {
if ($request->expectsJson()) {
return $this->fail('Already installed. if you want to reinstall, please remove installed.lock file.');
}
abort(404);
}
$extensions = collect([
['name' => 'BCMath', 'intro' => '数学精度处理拓展'],
['name' => 'Ctype', 'intro' => '字符类型检测拓展'],
['name' => 'DOM', 'intro' => 'DOM 对象模型,用于处理文档元素'],
['name' => 'Fileinfo', 'intro' => '获取文件信息拓展'],
['name' => 'JSON', 'intro' => 'JavaScript 对象符号JSON'],
['name' => 'Mbstring', 'intro' => '多字节字符串处理拓展'],
['name' => 'OpenSSL', 'intro' => '加密拓展'],
['name' => 'PCRE', 'intro' => '正则表达式拓展'],
['name' => 'PDO', 'intro' => '数据库拓展'],
['name' => 'Tokenizer', 'intro' => '令牌处理拓展'],
['name' => 'XML', 'intro' => 'Xml 解析器'],
['name' => 'Imagick', 'intro' => '高性能图片处理拓展'],
])->transform(function ($item) {
$item['result'] = extension_loaded(strtolower($item['name']));
return $item;
})->push([
'name' => 'readlink、symlink 函数',
'intro' => '读取、创建符号链接函数',
'result' => function_exists('readlink') && function_exists('symlink'),
])->push([
'name' => 'putenv、getenv 函数',
'intro' => '设置和获取环境变量函数',
'result' => function_exists('putenv') && function_exists('getenv'),
])->push([
'name' => 'exec、shell_exec 函数',
'intro' => '执行外部命令',
'result' => function_exists('exec') && function_exists('shell_exec'),
])->push([
'name' => 'chmod、chown、fileperms 函数',
'intro' => '设置和获取文件、文件夹权限函数',
'result' => function_exists('chmod') && function_exists('chown') && function_exists('fileperms'),
])->push([
'name' => 'PHP >= 8.0.2',
'intro' => '最低要求 PHP 8.0.2 版本',
'result' => phpversion() >= 8,
]);
$status = ! $extensions->where('result', false)->isNotEmpty();
if ($request->method() === 'POST') {
try {
$request->validate([
'account.email' => 'required|email',
'account.password' => 'required|between:6,32'
], [], [
'account.email' => '管理员账号邮箱',
'account.password' => '管理员账号密码'
]);
$data = collect($request->only([
'connection', 'host', 'port', 'database', 'username', 'password',
]))->transform(fn($item, $key) => ['--'.$key => $item])->collapse();
$output = new BufferedOutput();
$exitCode = Artisan::call('lsky:install', $data->toArray(), $output);
if ($exitCode) {
throw new \Exception(str_replace(PHP_EOL, '<br/>', $output->fetch()));
}
$user = new User([
'name' => '超级管理员',
'email' => $request->input('account.email'),
'password' => Hash::make($request->input('account.password')),
'registered_ip' => $request->ip(),
]);
$user->group_id = Group::query()->first()['id'];
$user->is_adminer = true;
$user->status = UserStatus::Normal;
$user->email_verified_at = date('Y-m-d H:i:s');
// 设置默认策略组的 url 为当前请求 url
Strategy::query()->update(['configs->url' => $request->getSchemeAndHttpHost().'/i']);
$user->save();
} catch (\Throwable $e) {
@unlink(base_path('installed.lock'));
Utils::e($e, '执行安装程序时出现异常');
return $this->fail($e->getMessage());
}
return $this->success();
}
return view('install', compact('extensions', 'status'));
}
public function upload(Request $request, ImageService $service): Response
{
try {
$image = $service->store($request);
} catch (UploadException $e) {
return $this->fail($e->getMessage());
} catch (\Throwable $e) {
Utils::e($e, 'Web 上传文件时发生异常');
if (config('app.debug')) {
return $this->fail($e->getMessage());
}
return $this->fail('服务异常,请稍后再试');
}
return $this->success('上传成功', $image->setAppends(['pathname', 'links'])->only(
'id', 'pathname', 'origin_name', 'size', 'mimetype', 'md5', 'sha1', 'links'
));
}
public function output(Request $request, ImageService $service): StreamedResponse
{
/** @var Image $image */
$image = Image::query()
->with('group')
->where('key', $request->route('key'))
->where('extension', strtolower($request->route('extension')))
->firstOr(fn() => abort(404));
if (! $image->group?->configs->get(GroupConfigKey::IsEnableOriginalProtection)) {
abort(404);
}
try {
$cacheKey = "image_{$image->key}";
if (Cache::has($cacheKey)) {
$contents = Cache::get($cacheKey);
} else {
$contents = $image->filesystem()->read($image->pathname);
$configs = collect($image->group?->configs->get(GroupConfigKey::WatermarkConfigs));
// 是否启用了水印功能跳过gif和ico图片
if (
$image->group?->configs->get(GroupConfigKey::IsEnableWatermark) &&
$configs->get('mode', Mode::Overlay) == Mode::Dynamic &&
! in_array($image->extension, ['ico', 'gif', 'svg'])
) {
$quality = $image->group?->configs->get(GroupConfigKey::ImageSaveQuality, 75);
$contents = $service->stickWatermark($contents, $configs)->encode($image->extension, $quality)->getEncoded();
}
$cacheTtl = (int)$image->group?->configs->get(GroupConfigKey::ImageCacheTtl, 0);
// 是否启用了缓存
if ($cacheTtl) {
Cache::remember($cacheKey, $cacheTtl, fn () => $contents);
} else {
if (Cache::has($cacheKey)) Cache::forget($cacheKey);
}
}
} catch (FilesystemException $e) {
Utils::e($e, '图片输出时出现异常');
abort(404);
}
$mimetype = $image->mimetype;
// ico svg 图片直接输出,不经过 InterventionImage 处理
if (in_array($image->extension, ['ico', 'svg'])) {
goto out;
}
// 浏览器无法预览的图片,改为 png 格式输出
if (in_array($image->extension, ['psd', 'tif', 'bmp'])) {
$mimetype = 'image/png';
$contents = InterventionImage::make($contents)->encode('png')->getEncoded();
}
out:
return \response()->stream(function () use ($contents) {
echo $contents;
}, headers: ['Content-type' => $mimetype]);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\AlbumRequest;
use App\Models\Album;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class AlbumController extends Controller
{
public function albums(): Response
{
/** @var User $user */
$user = Auth::user();
$albums = $user->albums()->latest()->paginate(40);
$albums->getCollection()->each(function (Album $album) {
$album->setVisible(['id', 'name', 'intro', 'image_num']);
});
return $this->success('success', compact('albums'));
}
public function create(AlbumRequest $request): Response
{
/** @var User $user */
$user = Auth::user();
DB::transaction(function () use ($user, $request) {
$user->albums()->create(array_filter($request->validated()));
$user->album_num = $user->albums()->count();
$user->save();
});
return $this->success('创建成功');
}
public function update(AlbumRequest $request): Response
{
/** @var User $user */
$user = Auth::user();
$album = $user->albums()->find($request->route('id'));
if (is_null($album)) {
return $this->fail('不存在的相册');
}
$album->update(array_filter($request->validated()));
return $this->success('修改成功');
}
public function delete(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
/** @var Album|null $album */
$album = $user->albums()->find($request->route('id'));
if (is_null($album)) {
return $this->fail('不存在的相册');
}
DB::transaction(function () use ($user, $album) {
$album->images()->update(['album_id' => null]);
$album->delete();
$user->album_num = $user->albums()->count();
$user->save();
});
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace App\Http\Controllers\User;
use App\Enums\ImagePermission;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageRenameRequest;
use App\Models\Album;
use App\Models\Image;
use App\Models\User;
use App\Services\UserService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class ImageController extends Controller
{
public function index(): View
{
return view('user.images');
}
public function images(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
$images = $user->images()->filter($request)->with('group', 'strategy')->paginate(40);
$images->getCollection()->each(function (Image $image) {
// 图片宽高过小会导致前端排版异常
$image->width = max($image->width, 200);
$image->height = max($image->height, 200);
$image->human_date = $image->created_at->diffForHumans();
$image->date = $image->created_at->format('Y-m-d H:i:s');
$image->append(['url', 'thumb_url', 'filename', 'links'])->setVisible([
'id', 'filename', 'url', 'thumb_url', 'human_date', 'date', 'size', 'width', 'height', 'links'
]);
});
return $this->success('success', compact('images'));
}
public function image(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
/** @var Image $image */
if (!$image = $user->images()->find($request->route('id'))) {
return $this->fail('未找到该图片');
}
$image->strategy?->setVisible(['name']);
$image->album?->setVisible(['name']);
$image->append(['url', 'thumb_url', 'filename', 'links'])->setVisible([
'id', 'filename', 'origin_name', 'url', 'thumb_url', 'width', 'height', 'size', 'mimetype', 'md5', 'sha1',
'permission', 'strategy', 'album', 'uploaded_ip', 'links', 'created_at'
]);
return $this->success('success', compact('image'));
}
public function permission(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
$permission = $request->input('permission');
$permissions = ['public' => ImagePermission::Public, 'private' => ImagePermission::Private];
if (!in_array($permission, array_keys($permissions))) {
return $this->fail('设置失败');
}
$user->images()->whereIn('id', (array) $request->input('ids'))->update([
'permission' => $permissions[$permission],
]);
return $this->success('设置成功');
}
public function rename(ImageRenameRequest $request): Response
{
/** @var User $user */
$user = Auth::user();
/** @var Image $image */
if ($image = $user->images()->find($request->input('id'))) {
$image->alias_name = $request->input('name');
$image->save();
}
return $this->success('重命名成功', $image->only('id', 'filename'));
}
public function movement(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
DB::transaction(function () use ($user, $request) {
/** @var null|Album $album */
$album = $user->albums()->find((int) $request->input('id'));
$user->images()->whereIn('id', $request->input('selected'))->update([
'album_id' => $album->id ?? null,
]);
if ($album) {
$album->image_num = $album->images()->count();
$album->save();
}
if ($albumId = (int) $request->input('album_id')) {
/** @var Album $originAlbum */
$originAlbum = $user->albums()->find($albumId);
$originAlbum->image_num = $originAlbum->images()->count();
$originAlbum->save();
}
});
return $this->success('移动成功');
}
public function delete(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
(new UserService())->deleteImages($request->all() ?: [], $user);
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\User;
use App\Enums\UserConfigKey;
use App\Http\Controllers\Controller;
use App\Http\Requests\UserSettingRequest;
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\View;
class UserController extends Controller
{
public function dashboard(): View
{
/** @var User $user */
$user = Auth::user();
$configs = $user->group->configs;
$strategies = $user->group->strategies()->get();
return view('user.dashboard', compact('strategies', 'configs', 'user'));
}
public function settings(): View
{
return view('user.settings');
}
public function update(UserSettingRequest $request): Response
{
/** @var User $user */
$user = Auth::user();
$user->name = $request->validated('name');
$user->url = $request->validated('url') ?: '';
$user->configs = $user->configs->merge(collect($request->validated('configs'))->transform(function ($value) {
return (int)$value;
}));
if ($password = $request->validated('password')) {
$user->forceFill([
'password' => Hash::make($password),
'remember_token' => Str::random(60),
]);
event(new PasswordReset($user));
}
$user->save();
return $this->success('保存成功');
}
public function setStrategy(Request $request): Response
{
/** @var User $user */
$user = Auth::user();
if (! $strategy = $user->group->strategies()->find($request->id)) {
return $this->fail('没有找到该策略');
}
$user->update(['configs->'.UserConfigKey::DefaultStrategy => $strategy->id]);
return $this->success('设置成功');
}
}

68
app/Http/Kernel.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.admin' => \App\Http\Middleware\AuthenticateWithAdmin::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Support\Facades\Auth;
class AuthenticateWithAdmin extends Authenticate
{
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($request, $guards);
/** @var User $user */
$user = Auth::user();
if (! $user->is_adminer) {
return abort(403);
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use App\Enums\ConfigKey;
use App\Http\Result;
use App\Utils;
use Closure;
use Illuminate\Http\Request;
class CheckIsEnableApi
{
use Result;
public function handle(Request $request, Closure $next)
{
if (! Utils::config(ConfigKey::IsEnableApi)) {
if ($request->expectsJson()) {
return $this->fail('管理员未启用 API')->setStatusCode(403);
}
abort(404);
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use App\Enums\ConfigKey;
use App\Http\Result;
use App\Utils;
use Closure;
use Illuminate\Http\Request;
class CheckIsEnableGallery
{
use Result;
public function handle(Request $request, Closure $next)
{
if (! Utils::config(ConfigKey::IsEnableGallery)) {
if ($request->expectsJson()) {
return $this->fail('管理员未启用画廊功能')->setStatusCode(403);
}
abort(404);
}
return $next($request);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware;
use App\Enums\ConfigKey;
use App\Http\Result;
use App\Utils;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class CheckIsEnableGuestUpload
{
use Result;
public function handle(Request $request, Closure $next)
{
// 检测是否启用了游客上传,未启用、未登录情况下跳转至登录页面
if (! Utils::config(ConfigKey::IsAllowGuestUpload) && Auth::guest()) {
return redirect('login');
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use App\Enums\ConfigKey;
use App\Http\Result;
use App\Utils;
use Closure;
use Illuminate\Http\Request;
class CheckIsEnableRegistration
{
use Result;
public function handle(Request $request, Closure $next)
{
if (! Utils::config(ConfigKey::IsEnableRegistration)) {
if ($request->expectsJson()) {
return $this->fail('站点管理员关闭了注册功能')->setStatusCode(403);
}
abort(404);
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use App\Http\Result;
use Closure;
use Illuminate\Http\Request;
class CheckIsInstalled
{
use Result;
public function handle(Request $request, Closure $next)
{
// 检测程序是否安装
if (! file_exists(base_path('installed.lock'))) {
if (! $request->expectsJson()) {
return redirect('install');
} else {
return $this->fail('It has already been installed.');
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,165 @@
<?php
namespace App\Http\Requests\Admin;
use App\Http\Requests\FormRequest;
use Illuminate\Validation\Rule;
class GroupRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$requiredIfReview = function ($driver) {
return Rule::requiredIf($this->input('configs.is_enable_scan') && $this->input('configs.scan_configs.driver') === $driver);
};
$requiredIfWatermark = function ($driver) {
return Rule::requiredIf($this->input('configs.is_enable_watermark') && $this->input('configs.watermark_configs.driver') === $driver);
};
return [
'name' => 'required|between:2,30',
'is_default' => 'boolean',
'is_guest' => 'boolean',
'configs' => 'required|array',
'configs.maximum_file_size' => 'required|numeric',
'configs.concurrent_upload_num' => 'required|integer',
'configs.limit_per_minute' => 'required|integer',
'configs.limit_per_hour' => 'required|integer',
'configs.limit_per_day' => 'required|integer',
'configs.limit_per_week' => 'required|integer',
'configs.limit_per_month' => 'required|integer',
'configs.image_save_quality' => 'required|min:1|max:100',
'configs.image_save_format' => '',
'configs.path_naming_rule' => 'max:400',
'configs.file_naming_rule' => 'max:400',
'configs.accepted_file_suffixes' => 'required|array|in:jpeg,jpg,png,gif,tif,bmp,ico,psd,webp,svg',
'configs.is_enable_scan' => 'boolean',
'configs.scanned_action' => [
'exclude_if:configs.is_enable_scan,false',
'in:mark,delete',
],
'configs.scan_configs.driver' => ['exclude_if:configs.is_enable_scan,false', 'in:tencent,aliyun,nsfwjs'],
'configs.scan_configs.drivers.tencent.endpoint' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.secret_id' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.secret_key' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.region' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.biz_type' => '',
'configs.scan_configs.drivers.aliyun.access_key_id' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.access_key_secret' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.region_id' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.biz_type' => '',
'configs.scan_configs.drivers.aliyun.scenes' => [$requiredIfReview('aliyun'), 'array'],
'configs.scan_configs.drivers.nsfwjs.api_url' => [$requiredIfReview('nsfwjs')],
'configs.scan_configs.drivers.nsfwjs.attr_name' => [$requiredIfReview('nsfwjs'), 'nullable'],
'configs.scan_configs.drivers.nsfwjs.threshold' => [$requiredIfReview('nsfwjs'), 'nullable', 'integer', 'between:1,100'],
'configs.is_enable_original_protection' => 'boolean',
'configs.image_cache_ttl' => 'nullable|numeric',
'configs.is_enable_watermark' => 'boolean',
'configs.watermark_configs.mode' => ['in:1,2'],
'configs.watermark_configs.driver' => ['exclude_if:configs.is_enable_watermark,false', 'in:font,image'],
'configs.watermark_configs.drivers.font.font' => [
$requiredIfWatermark('font'),
function ($attribute, $value, $fail) {
if (! file_exists(storage_path('app/public/'.$value))) {
$fail('字体文件不存在');
}
},
],
'configs.watermark_configs.drivers.font.position' => [$requiredIfWatermark('font')],
'configs.watermark_configs.drivers.font.text' => [$requiredIfWatermark('font')],
'configs.watermark_configs.drivers.font.color' => [$requiredIfWatermark('font')],
'configs.watermark_configs.drivers.font.size' => [$requiredIfWatermark('font'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.font.angle' => [$requiredIfWatermark('font'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.font.x' => [$requiredIfWatermark('font'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.font.y' => [$requiredIfWatermark('font'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.image.image' => [
$requiredIfWatermark('image'),
function ($attribute, $value, $fail) {
if (! file_exists(storage_path('app/public/'.$value))) {
$fail('图片文件不存在');
}
},
],
'configs.watermark_configs.drivers.image.position' => [$requiredIfWatermark('image')],
'configs.watermark_configs.drivers.image.width' => [$requiredIfWatermark('image'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.image.height' => [$requiredIfWatermark('image'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.image.opacity' => [
$requiredIfWatermark('image'), 'nullable', 'integer', 'between:0,100',
],
'configs.watermark_configs.drivers.image.rotate' => [$requiredIfWatermark('image'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.image.x' => [$requiredIfWatermark('image'), 'nullable', 'integer'],
'configs.watermark_configs.drivers.image.y' => [$requiredIfWatermark('image'), 'nullable', 'integer'],
];
}
public function attributes()
{
return [
'name' => '组名称',
'is_default' => '是否默认',
'configs' => '组配置',
'configs.maximum_file_size' => '文件最大上传大小',
'configs.concurrent_upload_num' => '并发上传数量',
'configs.limit_per_minute' => '每分钟上传限制',
'configs.limit_per_hour' => '每小时上传限制',
'configs.limit_per_day' => '每天上传限制',
'configs.limit_per_week' => '每周上传限制',
'configs.limit_per_month' => '每月上传限制',
'configs.path_naming_rule' => '路径命名规则',
'configs.file_naming_rule' => '文件命名规则',
'configs.image_save_quality' => '图片保存质量',
'configs.image_save_format' => '图片保存格式',
'configs.accepted_file_suffixes' => '允许上传的文件后缀',
'configs.is_enable_scan' => '是否启用图片审核',
'configs.scanned_action' => '图片审核动作',
'configs.scan_configs.driver' => '图片审核驱动',
'configs.scan_configs.drivers.tencent.endpoint' => 'Endpoint',
'configs.scan_configs.drivers.tencent.secret_id' => 'SecretId',
'configs.scan_configs.drivers.tencent.secret_key' => 'SecretKey',
'configs.scan_configs.drivers.tencent.region' => '地域节点',
'configs.scan_configs.drivers.tencent.biz_type' => '业务场景',
'configs.scan_configs.drivers.aliyun.access_key_id' => 'AccessKeyId',
'configs.scan_configs.drivers.aliyun.access_key_secret' => 'AccessKeySecret',
'configs.scan_configs.drivers.aliyun.region_id' => '地域节点',
'configs.scan_configs.drivers.aliyun.biz_type' => '场景名称',
'configs.scan_configs.drivers.aliyun.scenes' => '审核场景',
'configs.scan_configs.drivers.nsfwjs.api_url' => '接口地址',
'configs.scan_configs.drivers.nsfwjs.attr_name' => '表单名称',
'configs.scan_configs.drivers.nsfwjs.threshold' => '阈值',
'configs.is_enable_original_protection' => '是否启用原图保护功能',
'configs.image_cache_ttl' => '图片缓存时间',
'configs.is_enable_watermark' => '是否启用水印功能',
'configs.watermark_configs.driver' => '水印驱动',
'configs.watermark_configs.drivers.font.font' => '字体文件',
'configs.watermark_configs.drivers.font.position' => '水印位置',
'configs.watermark_configs.drivers.font.text' => '水印文字',
'configs.watermark_configs.drivers.font.color' => '字体颜色',
'configs.watermark_configs.drivers.font.size' => '水印文字大小',
'configs.watermark_configs.drivers.font.angle' => '水印旋转角度',
'configs.watermark_configs.drivers.font.x' => '水印X轴偏移量',
'configs.watermark_configs.drivers.font.y' => '水印Y轴偏移量',
'configs.watermark_configs.drivers.image.image' => '水印图片文件',
'configs.watermark_configs.drivers.image.position' => '水印位置',
'configs.watermark_configs.drivers.image.width' => '水印图片宽度',
'configs.watermark_configs.drivers.image.height' => '水印图片高度',
'configs.watermark_configs.drivers.image.opacity' => '水印透明度',
'configs.watermark_configs.drivers.image.rotate' => '水印旋转角度',
'configs.watermark_configs.drivers.image.x' => '水印X轴偏移量',
'configs.watermark_configs.drivers.image.y' => '水印Y轴偏移量',
];
}
}

View File

@@ -0,0 +1,221 @@
<?php
namespace App\Http\Requests\Admin;
use App\Enums\StrategyKey;
use App\Http\Requests\FormRequest;
use App\Models\Strategy;
use Illuminate\Support\Collection;
class StrategyRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$checkUrl = function ($attribute, $value, $fail) {
if ($this->input('key') == StrategyKey::Local) {
$folders = [config('app.thumbnail_path'), 'fonts', 'css', 'js'];
$symlink = Strategy::getRootPath($value);
if (! $symlink) {
return $fail('访问域名缺少根路径');
}
if (in_array($symlink, $folders)) {
return $fail('系统保留路径:'. $symlink);
}
if (false !== strpbrk($symlink, "\\/?%*:|\"<>")) {
return $fail('根路径名称不符合规则');
}
// 修改时单独判断
if ($this->method() === 'PUT') {
$symlinks = Strategy::query()
->where('id', '<>', $this->route('id'))
->pluck('configs')
->transform(function (Collection $configs) {
return Strategy::getRootPath($configs->get('url'));
})->merge($folders)->values()->toArray();
if (in_array($symlink, $symlinks)) {
return $fail('根路径已经存在');
}
} else {
if (realpath(public_path($symlink))) {
return $fail('根路径已经存在');
}
}
}
};
$array = [
'groups' => 'array',
'name' => 'required|max:60',
'intro' => 'max:2000',
'key' => 'required|integer',
'configs.url' => ['required', 'url'],
'configs.queries' => '',
];
return array_merge($array, match((int)$this->input('key')) {
StrategyKey::Local => [
'configs.url' => ['required', 'url', $checkUrl],
'configs.root' => ['max:1000', function ($attribute, $value, $fail) {
if ($value) {
if (! is_dir($value)) {
return $fail('储存路径不存在');
}
if (! is_writeable($value)) {
return $fail('储存路径没有写入权限');
}
}
}],
],
StrategyKey::S3 => [
'configs.access_key_id' => 'required',
'configs.secret_access_key' => 'required',
'configs.endpoint' => '',
'configs.region' => '',
'configs.bucket' => 'required',
],
StrategyKey::Oss => [
'configs.access_key_id' => 'required',
'configs.access_key_secret' => 'required',
'configs.endpoint' => 'required',
'configs.bucket' => 'required',
],
StrategyKey::Cos => [
'configs.app_id' => 'required',
'configs.secret_id' => 'required',
'configs.secret_key' => 'required',
'configs.region' => 'required',
'configs.bucket' => 'required',
],
StrategyKey::Kodo => [
'configs.access_key' => 'required',
'configs.secret_key' => 'required',
'configs.bucket' => 'required',
],
StrategyKey::Uss => [
'configs.service' => 'required',
'configs.operator' => 'required',
'configs.password' => 'required',
],
StrategyKey::Sftp => [
'configs.root' => 'required',
'configs.host' => 'required',
'configs.port' => 'integer',
'configs.username' => 'required',
'configs.password' => 'required_if:configs.private_key,null',
'configs.private_key' => 'required_if:configs.password,null',
'configs.passphrase' => '',
'configs.use_agent' => 'required|boolean',
],
StrategyKey::Ftp => [
'configs.root' => 'required',
'configs.host' => 'required',
'configs.port' => 'integer',
'configs.username' => 'required',
'configs.password' => 'required',
'configs.ssl' => 'required|boolean',
'configs.passive' => 'required|boolean',
],
StrategyKey::Webdav => [
'configs.base_uri' => 'required',
'configs.username' => '',
'configs.password' => '',
'configs.auth_type' => '',
'configs.prefix' => '',
],
StrategyKey::Minio => [
'configs.access_key' => 'required',
'configs.secret_key' => 'required',
'configs.endpoint' => '',
'configs.region' => '',
'configs.bucket' => 'required',
'configs.bucket_endpoint' => '',
],
});
}
public function attributes()
{
$array = [
'name' => '名称',
'intro' => '简介',
'key' => '策略',
'configs.url' => '访问网址',
'configs.queries' => 'Url 额外参数',
];
return array_merge($array, match((int)$this->input('key')) {
StrategyKey::Local => [
'configs.root' => '根目录路径',
],
StrategyKey::S3 => [
'configs.access_key_id' => 'AccessKeyId',
'configs.secret_access_key' => 'SecretAccessKey',
'configs.endpoint' => '连接地址',
'configs.region' => '区域',
'configs.bucket' => '储存桶名称',
],
StrategyKey::Oss => [
'configs.access_key_id' => 'AccessKeyId',
'configs.access_key_secret' => 'AccessKeySecret',
'configs.endpoint' => '地域节点域名',
'configs.bucket' => 'Bucket 名称',
],
StrategyKey::Cos => [
'configs.app_id' => 'AppId',
'configs.secret_id' => 'SecretId',
'configs.secret_key' => 'SecretKey',
'configs.region' => '所属地域',
'configs.bucket' => '储存桶名称',
],
StrategyKey::Kodo => [
'configs.access_key' => 'AccessKey',
'configs.secret_key' => 'SecretKey',
'configs.bucket' => 'Bucket',
],
StrategyKey::Uss => [
'configs.service' => '服务名称',
'configs.operator' => '操作员名称',
'configs.password' => '操作员密码',
],
StrategyKey::Sftp => [
'configs.root' => '根目录路径',
'configs.host' => '主机地址',
'configs.port' => '连接端口',
'configs.username' => '用户名',
'configs.password' => '密码',
'configs.private_key' => '密钥',
'configs.passphrase' => '密钥口令',
'configs.use_agent' => '使用代理',
],
StrategyKey::Ftp => [
'configs.root' => '根目录路径',
'configs.host' => '主机地址',
'configs.port' => '连接端口',
'configs.username' => '用户名',
'configs.password' => '密码',
'configs.ssl' => '加密连接',
'configs.passive' => '被动模式',
],
StrategyKey::Webdav => [
'configs.base_uri' => '连接地址',
'configs.username' => '用户名',
'configs.password' => '密码',
'configs.auth_type' => '认证方式',
'configs.prefix' => '前缀',
],
StrategyKey::Minio => [
'configs.access_key' => 'AccessKey',
'configs.secret_key' => 'SecretKey',
'configs.endpoint' => '连接地址',
'configs.region' => '区域',
'configs.bucket' => 'Bucket 名称',
'configs.bucket_endpoint' => 'BucketEndpoint',
],
});
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests\Admin;
use App\Http\Requests\FormRequest;
use Illuminate\Validation\Rules;
class UserRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'group_id' => 'required',
'name' => 'required|between:2,30',
'capacity' => 'required|numeric',
'password' => ['nullable', Rules\Password::defaults()],
'status' => 'in:1,0'
];
}
public function attributes()
{
return [
'group_id' => '角色组',
'name' => '昵称',
'capacity' => '总容量',
'password' => '密码',
'status' => '状态',
];
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests;
class AlbumRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|max:60|alpha_dash',
'intro' => 'max:600'
];
}
public function messages()
{
return [
'name.required' => '名称不能为空',
'name.max' => '名称字符过长,最大不能超过 60',
'name.alpha_dash' => '名称只能是字母、数字,短破折号(-和下划线_',
'intro.max' => '简介字符过长,最大不能超过 600'
];
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
];
}
/**
* Attempt to authenticate the request's credentials.
*
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate()
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
/**
* Ensure the login request is not rate limited.
*
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
public function ensureIsNotRateLimited()
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout($this));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the rate limiting throttle key for the request.
*
* @return string
*/
public function throttleKey()
{
return Str::lower($this->input('email')).'|'.$this->ip();
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Requests;
use App\Http\Result;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
class FormRequest extends \Illuminate\Foundation\Http\FormRequest
{
use Result;
public function authorize()
{
return true;
}
protected function failedValidation(Validator $validator)
{
throw (new HttpResponseException($this->fail($validator->errors()->first())));
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Http\Requests;
class ImageRenameRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'id' => 'required|numeric',
'name' => 'required|max:50|string',
];
}
public function messages()
{
return [
'id.required' => '请选择一张图片',
'id.numeric' => '图片选择异常',
'name.required' => '请输入名称',
'name.max' => '名称长度不能超过 50 个字符',
'name.string' => '名称格式不正确',
];
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Http\Requests;
class UserSettingRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|between:2,20',
'url' => 'nullable|url',
'password' => 'nullable|between:6,32',
'configs' => 'array',
'configs.default_album' => 'required|numeric',
'configs.default_strategy' => 'required|numeric',
'configs.default_permission' => 'required|in:1,0',
'configs.pasted_action' => 'required|in:1,2',
'configs.is_auto_clear_preview' => 'nullable|boolean'
];
}
public function messages()
{
return [
'name.required' => '昵称不能为空',
'name.between' => '昵称必须在 2-20 个字符之间',
'url.url' => '个人主页地址格式不正确',
'password.between' => '密码必须在 6-32 个字符之间',
'configs.array' => '配置值不正确',
'configs.default_album.required' => '默认相册选择错误',
'configs.default_album.numeric' => '默认相册选择错误',
'configs.default_strategy.required' => '默认策略选择错误',
'configs.default_strategy.numeric' => '默认策略选择错误',
'configs.default_permission.required' => '权限值选择错误',
'configs.default_permission.in' => '权限值不正确',
'configs.pasted_action.required' => '粘贴动作值选择错误',
'configs.pasted_action.in' => '粘贴动作值不正确',
'configs.is_auto_clear_preview.boolean' => '是否自动清除预览选择错误'
];
}
}

24
app/Http/Result.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http;
use Illuminate\Http\Response;
trait Result
{
public function success(string $message = 'success', $data = []): Response
{
return $this->response(true, $message, $data);
}
public function fail(string $message = 'fail', $data = []): Response
{
return $this->response(false, $message, $data);
}
public function response(bool $status, string $message = '', $data = []): Response
{
$data = $data ?: new \stdClass;
return response(compact('status', 'message', 'data'));
}
}

33
app/Mail/Test.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class Test extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('emails.test');
}
}

75
app/Models/Album.php Normal file
View File

@@ -0,0 +1,75 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Http\Request;
/**
* @property int $id
* @property int $user_id
* @property string $name
* @property string $intro
* @property int $image_num
* @property-read User $user
* @property-read Collection $images
*/
class Album extends Model
{
use HasFactory;
protected $fillable = [
'name',
'intro',
'image_num'
];
protected $hidden = [
'user_id',
];
protected $attributes = [
'intro' => '',
];
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'image_num' => 'integer',
];
public function scopeFilter(Builder $builder, Request $request)
{
return $builder->when($request->query('order') ?: 'newest', function (Builder $builder, $order) {
switch ($order) {
case 'earliest':
$builder->orderBy('created_at');
break;
case 'most':
$builder->orderByDesc('image_num');
break;
case 'least':
$builder->orderBy('image_num');
break;
default:
$builder->latest();
}
})->when($request->query('keyword'), function (Builder $builder, $keyword) {
$builder->where('name', 'like', "%{$keyword}%")->orWhere('intro', 'like', "%{$keyword}%");
});
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
public function images(): HasMany
{
return $this->hasMany(Image::class, 'album_id', 'id');
}
}

18
app/Models/Config.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* @property string $name
* @property mixed $value
*/
class Config extends Model
{
use HasFactory;
public $incrementing = false;
protected $keyType = 'string';
}

101
app/Models/Group.php Normal file
View File

@@ -0,0 +1,101 @@
<?php
namespace App\Models;
use App\Utils;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
/**
* @property int $id
* @property string $name
* @property boolean $is_default
* @property boolean $is_guest
* @property Collection $configs
* @property Carbon $updated_at
* @property Carbon $created_at
* @property-read \Illuminate\Database\Eloquent\Collection $users
* @property-read \Illuminate\Database\Eloquent\Collection $images
* @property-read \Illuminate\Database\Eloquent\Collection $strategies
*/
class Group extends Model
{
use HasFactory;
protected $fillable = [
'name',
'is_default',
'is_guest',
'configs',
];
protected $casts = [
'id' => 'integer',
'is_default' => 'bool',
'is_guest' => 'bool',
'configs' => 'collection'
];
const POSITIONS = [
'top-left' => '左上角',
'top' => '上中',
'top-right' => '右上角',
'left' => '左边',
'center' => '中间',
'right' => '右边',
'bottom-left' => '左下角',
'bottom' => '下中',
'bottom-right' => '右下角',
'tiled' => '平铺',
];
const SCENES = [
'porn' => '智能鉴黄',
'terrorism' => '暴恐涉政',
'ad' => '图文违规',
'qrcode' => '二维码',
'live' => '不良场景',
'logo' => 'Logo',
];
protected static function booted()
{
static::saving(function (self $group) {
if ($group->isDirty('is_default') && $group->is_default) {
Group::query()->where('is_default', true)->update(['is_default' => false]);
}
if ($group->isDirty('is_guest') && $group->is_guest) {
Group::query()->where('is_guest', true)->update(['is_guest' => false]);
}
$group->configs = Utils::parseConfigs(self::getDefaultConfigs()->toArray(), $group->configs->toArray());
});
}
/**
* 获取组默认配置
*
* @return Collection
*/
public static function getDefaultConfigs(): Collection
{
return collect(config('convention.group'));
}
public function users(): HasMany
{
return $this->hasMany(User::class, 'group_id', 'id');
}
public function images(): HasMany
{
return $this->hasMany(Image::class, 'group_id', 'id');
}
public function strategies(): BelongsToMany
{
return $this->belongsToMany(Strategy::class, 'group_strategy', 'group_id', 'strategy_id');
}
}

254
app/Models/Image.php Normal file
View File

@@ -0,0 +1,254 @@
<?php
namespace App\Models;
use App\Enums\GroupConfigKey;
use App\Enums\ImagePermission;
use App\Services\ImageService;
use App\Utils;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use League\Flysystem\Filesystem;
/**
* @property int $id
* @property int $user_id
* @property int $album_id
* @property int $group_id
* @property int $strategy_id
* @property string $key
* @property string $path
* @property string $name
* @property string $pathname
* @property string $origin_name
* @property string $alias_name
* @property string $filename
* @property float $size
* @property string $mimetype
* @property string $extension
* @property string $md5
* @property string $sha1
* @property integer $width
* @property integer $height
* @property string $url
* @property string $thumb_url
* @property Collection $links
* @property int $permission
* @property boolean $is_unhealthy
* @property string $uploaded_ip
* @property Carbon $updated_at
* @property Carbon $created_at
* @property-read User $user
* @property-read Album $album
* @property-read Group $group
* @property-read Strategy $strategy
*/
class Image extends Model
{
use HasFactory;
protected $fillable = [
'key',
'path',
'name',
'origin_name',
'alias_name',
'size',
'mimetype',
'extension',
'md5',
'sha1',
'width',
'height',
'permission',
'is_unhealthy',
'uploaded_ip',
];
protected $hidden = [
'user_id',
'album_id',
'group_id',
'strategy_id',
];
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'album_id' => 'integer',
'group_id' => 'integer',
'strategy_id' => 'integer',
'width' => 'integer',
'height' => 'integer',
'size' => 'float',
'is_unhealthy' => 'bool',
'permission' => 'integer',
];
protected ?Filesystem $filesystem = null;
protected static function booted()
{
static::creating(function (self $image) {
$image->key = $image->generateKey();
});
static::deleting(function (self $image) {
// TODO 检测是否启用了队列,放置队列中异步删除
// 在当前图片所属的策略中是否存在其他相同 md5 和 sha1 的记录,没有则可以删除物理文件
if (!static::query()
->where('strategy_id', $image->strategy_id)
->where('id', '<>', $image->id)
->where('md5', $image->md5)
->where('sha1', $image->sha1)
->exists()
) {
// 删除本地缓存文件
try {
// 删除物理文件
$image->filesystem()->delete($image->pathname);
@unlink(public_path($image->getThumbnailPathname()));
// 删除缓存
Cache::forget("image_{$image->key}");
} catch (\Throwable $e) {
Utils::e($e, '删除物理文件时发生异常');
}
}
});
}
public function scopeFilter(Builder $builder, Request $request)
{
return $builder->when($request->query('order') ?: 'newest', function (Builder $builder, $order) {
switch ($order) {
case 'earliest':
$builder->orderBy('created_at');
break;
case 'utmost':
$builder->orderByDesc('size');
break;
case 'least':
$builder->orderBy('size');
break;
default:
$builder->latest();
}
})->when($request->query('permission') ?: 'all', function (Builder $builder, $permission) {
switch ($permission) {
case 'public':
$builder->where('permission', ImagePermission::Public);
break;
case 'private':
$builder->where('permission', ImagePermission::Private);
break;
}
})->when($request->query('keyword'), function (Builder $builder, $keyword) {
$builder->where('origin_name', 'like', "%{$keyword}%")->orWhere('alias_name', 'like', "%{$keyword}%");
})->when((int) $request->query('album_id'), function (Builder $builder, $albumId) {
$builder->where('album_id', $albumId);
}, function (Builder $builder) {
$builder->whereNull('album_id');
});
}
public function filename(): Attribute
{
return new Attribute(fn() => $this->alias_name ?: $this->origin_name);
}
public function pathname(): Attribute
{
$path = $this->path ? "{$this->path}/" : '';
return new Attribute(fn() => "{$path}{$this->name}");
}
public function url(): Attribute
{
return new Attribute(function () {
// 是否启用原图保护功能
if ($this->group?->configs->get(GroupConfigKey::IsEnableOriginalProtection)) {
$url = asset("{$this->key}.{$this->extension}");
} else {
$url = rtrim($this->strategy?->configs->get('url'), '/').'/'.ltrim($this->pathname, '/');
}
// 拼接图片 url
return $url.($this->strategy?->configs->get('queries') ?: '');
});
}
public function thumbUrl(): Attribute
{
return new Attribute(function () {
$pathname = $this->getThumbnailPathname();
// 没有缩略图则返回原图
if (! file_exists(public_path($pathname))) {
return $this->url;
}
return asset($pathname);
});
}
public function links(): Attribute
{
return new Attribute(fn() => collect([
'url' => $this->url,
'html' => "&lt;img src=\"{$this->url}\" alt=\"{$this->origin_name}\" title=\"{$this->origin_name}\" /&gt;",
'bbcode' => "[img]{$this->url}[/img]",
'markdown' => "![{$this->origin_name}]({$this->url})",
'markdown_with_link' => "[![{$this->origin_name}]({$this->url})]({$this->url})",
'thumbnail_url' => $this->thumb_url,
]));
}
public function filesystem(): Filesystem
{
if (is_null($this->filesystem)) {
$this->filesystem = new Filesystem((new ImageService())->getAdapter($this->strategy));
}
return $this->filesystem;
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
public function album(): BelongsTo
{
return $this->belongsTo(Album::class, 'album_id', 'id');
}
public function group(): BelongsTo
{
return $this->belongsTo(Group::class, 'group_id', 'id');
}
public function strategy(): BelongsTo
{
return $this->belongsTo(Strategy::class, 'strategy_id', 'id');
}
public function getThumbnailPathname(): string
{
return trim(config('app.thumbnail_path'), '/')."/{$this->md5}.". ($this->extension === 'svg' ? 'svg' : "png");
}
private function generateKey($length = 6): string
{
$key = Str::random($length);
if (self::query()->where('key', $key)->exists()) {
return $this->generateKey(++$length);
}
return $key;
}
}

13
app/Models/Model.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Carbon\CarbonInterface;
abstract class Model extends \Illuminate\Database\Eloquent\Model
{
protected function serializeDate(\DateTimeInterface $date): string
{
return $date->format(CarbonInterface::DEFAULT_TO_STRING_FORMAT);
}
}

120
app/Models/Strategy.php Normal file
View File

@@ -0,0 +1,120 @@
<?php
namespace App\Models;
use App\Enums\StrategyKey;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Carbon;
use Sabre\DAV\Client;
/**
* @property int $id
* @property int $key
* @property string $name
* @property string $intro
* @property \Illuminate\Support\Collection $configs
* @property Carbon $updated_at
* @property Carbon $created_at
* @property-read Collection $groups
* @property-read Collection $images
*/
class Strategy extends Model
{
use HasFactory;
protected $fillable = [
'key',
'name',
'intro',
'configs',
];
protected $attributes = [
'intro' => '',
];
protected $casts = [
'id' => 'integer',
'key' => 'integer',
'configs' => 'collection',
];
const DRIVERS = [
StrategyKey::Local => '本地',
StrategyKey::S3 => 'AWS S3',
StrategyKey::Oss => '阿里云 OSS',
StrategyKey::Cos => '腾讯云 COS',
StrategyKey::Kodo => '七牛云 Kodo',
StrategyKey::Uss => '又拍云 USS',
StrategyKey::Sftp => 'SFTP',
StrategyKey::Ftp => 'FTP',
StrategyKey::Webdav => 'WebDav',
StrategyKey::Minio => 'Minio',
];
const WEBDAV_AUTH_TYPES = [
'' => 'Auto',
Client::AUTH_BASIC => 'Basic',
Client::AUTH_DIGEST => 'Digest',
Client::AUTH_NTLM => 'Ntlm',
];
protected static function booted()
{
static::saving(function (self $strategy) {
$strategy->configs['root'] = $strategy->configs->get('root');
$strategy->configs['url'] = rtrim($strategy->configs->get('url'), '/');
if ($strategy->key == StrategyKey::Local) {
$symlink = self::getRootPath($strategy->configs['url']);
$target = $strategy->configs['root'] ?: config('filesystems.disks.uploads.root');
if (! is_dir(public_path($symlink))) {
(new Filesystem())->link($target, $symlink);
}
// 是否需要移除旧的符号链接
$url = $strategy->getOriginal('configs')['url'] ?? '';
if ($url) {
$oldSymlink = self::getRootPath($url);
if ($oldSymlink != $symlink) {
@unlink($oldSymlink);
}
}
}
});
static::deleted(function (self $strategy) {
// 如果是本地策略,删除的时候同时删除符号连接
if ($strategy->key === StrategyKey::Local) {
$symlink = self::getRootPath($strategy->configs['url']);
@unlink(public_path($symlink));
}
});
}
public static function getRootPath($url): string
{
$path = (parse_url($url)['path'] ?? '');
return current(array_filter(explode('/', $path)));
}
public function intro(): Attribute
{
return new Attribute(
set: fn ($value) => $value ?: '',
);
}
public function groups(): BelongsToMany
{
return $this->belongsToMany(Group::class, 'group_strategy', 'strategy_id', 'group_id');
}
public function images(): HasMany
{
return $this->hasMany(Image::class, 'strategy_id', 'id');
}
}

130
app/Models/User.php Normal file
View File

@@ -0,0 +1,130 @@
<?php
namespace App\Models;
use App\Enums\ConfigKey;
use App\Utils;
use Illuminate\Support\Carbon;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Collection;
use Laravel\Sanctum\HasApiTokens;
/**
* @property int $id
* @property int $group_id
* @property string $name
* @property string $email
* @property string $password
* @property string $remember_token
* @property boolean $is_adminer
* @property float $capacity
* @property float $use_capacity
* @property string $url
* @property Collection $configs
* @property int $image_num
* @property int $album_num
* @property string $registered_ip
* @property int $status
* @property Carbon $email_verified_at
* @property Carbon $updated_at
* @property Carbon $created_at
* @property-read string $avatar
* @property-read Group $group
* @property-read \Illuminate\Database\Eloquent\Collection $albums
* @property-read \Illuminate\Database\Eloquent\Collection $images
*/
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'url',
'capacity',
'configs',
'configs->default_strategy',
'registered_ip',
'status',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
'configs',
'group_id',
'is_adminer',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'id' => 'integer',
'group_id' => 'integer',
'image_num' => 'integer',
'album_num' => 'integer',
'status' => 'integer',
'capacity' => 'float',
'is_adminer' => 'bool',
'configs' => 'collection',
'email_verified_at' => 'datetime',
];
protected $appends = ['avatar'];
protected static function booted()
{
static::creating(function (self $user) {
// 默认组
$user->group_id = Group::query()->where('is_default', true)->value('id');
// 初始容量
$user->capacity = Utils::config(ConfigKey::UserInitialCapacity);
$user->configs = collect(config('convention.user'))->merge($user->configs ?: []);
});
}
public function avatar(): Attribute
{
return new Attribute(fn () => Utils::getAvatar($this->email));
}
public function useCapacity(): Attribute
{
return new Attribute(fn () => $this->images()->sum('size'));
}
public function group(): BelongsTo
{
return $this->belongsTo(Group::class, 'group_id', 'id');
}
public function albums(): HasMany
{
return $this->hasMany(Album::class, 'user_id', 'id');
}
public function images(): HasMany
{
return $this->hasMany(Image::class, 'user_id', 'id');
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Providers;
use App\Enums\ConfigKey;
use App\Models\Group;
use App\Utils;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
// 是否需要生成 env 文件
if (! file_exists(base_path('.env'))) {
file_put_contents(base_path('.env'), file_get_contents(base_path('.env.example')));
// 生成 key
Artisan::call('key:generate');
}
// 如果已经安装程序,初始化一些配置
if (file_exists(base_path('installed.lock'))) {
// 覆盖默认配置
Config::set('app.name', Utils::config(ConfigKey::AppName));
Config::set('mail', array_merge(\config('mail'), Utils::config(ConfigKey::Mail)->toArray()));
View::composer('*', function (\Illuminate\View\View $view) {
/** @var Group $group */
$group = Auth::check() ? Auth::user()->group : Group::query()->where('is_guest', true)->first();
$view->with([
'_group' => $group,
'_is_notice' => strip_tags(Utils::config(ConfigKey::SiteNotice)),
]);
});
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
}

Some files were not shown because too many files have changed in this diff Show More