no message
This commit is contained in:
commit
65ca290e0b
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[compose.yaml]
|
||||
indent_size = 4
|
||||
65
.env.example
Normal file
65
.env.example
Normal file
@ -0,0 +1,65 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
# PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpactor.json
|
||||
.phpunit.result.cache
|
||||
/.fleet
|
||||
/.idea
|
||||
/.nova
|
||||
/.phpunit.cache
|
||||
/.vscode
|
||||
/.zed
|
||||
/auth.json
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/storage/pail
|
||||
/vendor
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
Thumbs.db
|
||||
!/_env/*
|
||||
59
README.md
Executable file
59
README.md
Executable file
@ -0,0 +1,59 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Redberry](https://redberry.international/laravel-development)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
85
_env/.env
Executable file
85
_env/.env
Executable file
@ -0,0 +1,85 @@
|
||||
token_base = wocaonide
|
||||
is_loc = true
|
||||
url_master_base = dd.loc
|
||||
site_code = test.com
|
||||
site_code_short = tt
|
||||
page_limit = 2
|
||||
app_title = SSR/V2ray/Clash免费节点每天更新 - v2rayfreenode
|
||||
app_desc = SSR/V2ray/Clash免费节点每天更新
|
||||
app_keywords = v2rayclashfree,Clash,Quantumult,Shadowrocket,SSR,v2ray,免费节点,节点,订阅链接
|
||||
url_resource_base = resource.loc
|
||||
|
||||
SCOUT_DRIVER=meilisearch
|
||||
MEILISEARCH_HOST=http://127.0.0.1:7700
|
||||
SCOUT_QUEUE=false
|
||||
|
||||
VITE_DEV_SERVER_URL=http://127.0.0.1:5173
|
||||
|
||||
APP_NAME=Laravelenv
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:VkMoebPURG4A1ker22M5nfFV+tcjph+eYvQrJYANq0Y=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://fn-b.loc
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
# PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=fn_b
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=root
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
RESPONSE_CACHE_ENABLED=false
|
||||
CACHE_STORE=file
|
||||
RESPONSE_CACHE_STORE=file
|
||||
RESPONSE_CACHE_DRIVER=file
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
584
_env/sql.sql
Normal file
584
_env/sql.sql
Normal file
@ -0,0 +1,584 @@
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
-- MariaDB dump 10.19-11.6.2-MariaDB, for osx10.19 (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: fn_b
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 11.6.2-MariaDB
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `article_freenode_thumb_nums`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `article_freenode_thumb_nums`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `article_freenode_thumb_nums` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`num` int(11) DEFAULT 1,
|
||||
`author_code` varchar(100) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `num` (`num`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `article_freenode_thumb_nums`
|
||||
--
|
||||
|
||||
LOCK TABLES `article_freenode_thumb_nums` WRITE;
|
||||
/*!40000 ALTER TABLE `article_freenode_thumb_nums` DISABLE KEYS */;
|
||||
INSERT INTO `article_freenode_thumb_nums` VALUES
|
||||
(2,2,'tg_xiuren_a');
|
||||
/*!40000 ALTER TABLE `article_freenode_thumb_nums` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `article_likes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `article_likes`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `article_likes` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`iArticleId` bigint(20) unsigned NOT NULL COMMENT '文章ID',
|
||||
`sIp` varchar(45) NOT NULL COMMENT '支持IPv4和IPv6',
|
||||
`sFingerprint` varchar(64) NOT NULL COMMENT '浏览器唯一指纹(建议存储MD5或SHA1)',
|
||||
`created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uidx_article_fingerprint` (`iArticleId`,`sFingerprint`),
|
||||
KEY `idx_article_likes_sip` (`sIp`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='游客点赞记录表';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `article_likes`
|
||||
--
|
||||
|
||||
LOCK TABLES `article_likes` WRITE;
|
||||
/*!40000 ALTER TABLE `article_likes` DISABLE KEYS */;
|
||||
INSERT INTO `article_likes` VALUES
|
||||
(6,1,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-01 02:03:19','2026-02-01 02:03:19'),
|
||||
(7,2,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-01 02:57:19','2026-02-01 02:57:19'),
|
||||
(9,26,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-02 00:03:59','2026-02-02 00:03:59'),
|
||||
(10,10,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-02 00:05:55','2026-02-02 00:05:55'),
|
||||
(11,33,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-05 20:47:30','2026-02-05 20:47:30'),
|
||||
(12,34,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-06 00:38:54','2026-02-06 00:38:54'),
|
||||
(13,35,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-07 00:51:18','2026-02-07 00:51:18'),
|
||||
(14,9,'127.0.0.1','bfed8360f6c1fc7039f49002245377d6','2026-02-07 00:55:46','2026-02-07 00:55:46');
|
||||
/*!40000 ALTER TABLE `article_likes` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `article_tag`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `article_tag`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `article_tag` (
|
||||
`iArticleId` int(11) unsigned NOT NULL,
|
||||
`iTagId` int(11) unsigned NOT NULL,
|
||||
KEY `article_tag_idx` (`iArticleId`,`iTagId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `article_tag`
|
||||
--
|
||||
|
||||
LOCK TABLES `article_tag` WRITE;
|
||||
/*!40000 ALTER TABLE `article_tag` DISABLE KEYS */;
|
||||
INSERT INTO `article_tag` VALUES
|
||||
(35,5),
|
||||
(35,6),
|
||||
(35,7),
|
||||
(35,8),
|
||||
(35,9),
|
||||
(35,10),
|
||||
(35,11),
|
||||
(35,12),
|
||||
(36,5),
|
||||
(36,6),
|
||||
(36,7),
|
||||
(36,8),
|
||||
(36,9),
|
||||
(36,10),
|
||||
(36,11),
|
||||
(36,12);
|
||||
/*!40000 ALTER TABLE `article_tag` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `articles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `articles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `articles` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`iUserId` bigint(20) unsigned DEFAULT NULL,
|
||||
`iCategoryId` bigint(20) unsigned NOT NULL COMMENT '所属分类ID',
|
||||
`sTitle` varchar(200) NOT NULL COMMENT '标题',
|
||||
`sTitleSub` varchar(255) DEFAULT NULL,
|
||||
`sSlug` varchar(200) DEFAULT NULL COMMENT '文章别名',
|
||||
`sContent` longtext NOT NULL COMMENT '正文',
|
||||
`sThumb` varchar(255) DEFAULT NULL COMMENT '缩略图',
|
||||
`iViews` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '阅读数',
|
||||
`iStatus` tinyint(4) NOT NULL DEFAULT 1 COMMENT '状态:1发布 0草稿',
|
||||
`iIsTop` tinyint(4) DEFAULT 0,
|
||||
`iLikeCount` int(11) DEFAULT 0,
|
||||
`i7like` int(11) DEFAULT 0,
|
||||
`i365like` int(11) DEFAULT 0,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `sSlug` (`sSlug`),
|
||||
KEY `articles_icategoryid_index` (`iCategoryId`),
|
||||
KEY `articles_istatus_index` (`iStatus`),
|
||||
KEY `idx_i365like` (`i365like`),
|
||||
KEY `iUserId` (`iUserId`),
|
||||
CONSTRAINT `articles_category_fk` FOREIGN KEY (`iCategoryId`) REFERENCES `categories` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `articles`
|
||||
--
|
||||
|
||||
LOCK TABLES `articles` WRITE;
|
||||
/*!40000 ALTER TABLE `articles` DISABLE KEYS */;
|
||||
INSERT INTO `articles` VALUES
|
||||
(33,1,1,'「2月5日」2026年最新高速SSR/V2ray/Clash/Shadowrocket免费节点订阅链接','本次更新共50个可用节点,最高速度5M/S。\n覆盖美国、美国、香港、乌克兰、阿根廷、土耳其等多个区域。\n复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。','fn20260205','加入tg频道获取更多新鲜免费节点:<a href=\"https://t.me/v2rayfreenode_com\">https://t.me/v2rayfreenode_com</a>。\n\n本次更新共50个可用节点,最高速度5M/S。\n覆盖美国、美国、香港、乌克兰、阿根廷、土耳其等多个区域。\n复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。\n\n机场推荐:\n\n* 好好 - 就是好\n\n* 不好不好 - 就是不好\n\n* 你就是个瘠薄 - 知道不\n\n* 去死吧 - 去死\n\n免费节点订阅链接:\n\nv2ray订阅链接:\n\nhttps://nodehub.loc/vfn/5e08dd26fff8100b/20260205-v2ray.txt\n\nclash订阅链接:\n\nhttps://nodehub.loc/vfn/5e08dd26fff8100b/20260205-clash.yaml\n\n温馨提示:\n\n本站提供的都是免费且公共的节点,稳定性与长时间连接速率无法与收费版的高速机场节点相提并论,不能奢望太多。\n\n注意你自己的网络环境(本地连接当中的DNS,手动配置一下:114.114.114.114,8.8.8.8或是其它公共的DNS。)\n\n如果某个网址无法代理访问,可切换到全局代理模式,一般可解决。',NULL,0,1,0,1,NULL,NULL,'2026-02-05 02:04:40','2026-02-05 20:47:30'),
|
||||
(34,1,1,'「2月6日」2026年最新高速SSR/V2ray/Clash/Shadowrocket免费节点订阅链接','本次更新共24个可用节点,最高速度8M/S。\n覆盖越南、瑞士、荷兰、泰国、美国、香港等多个区域。\n复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。','fn20260206','<p><b>加入tg频道获取更多新鲜免费节点:<a target=\"__blank\" style=\"color:blue;\" href=\"https://t.me/t.me/v2rayfreenode_com\">t.me/v2rayfreenode_com</a>。</b></p><p></p><p>本次更新共24个可用节点,最高速度8M/S。</p><p>覆盖越南、瑞士、荷兰、泰国、美国、香港等多个区域。</p><p>复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。</p><p></p><p><h2>机场推荐:</h2></p><p></p><p>* 好好 - 就是好</p><p></p><p>* 不好不好 - 就是不好</p><p></p><p>* 你就是个瘠薄 - 知道不</p><p></p><p>* 去死吧 - 去死</p><p></p><p><b><a target=\'__blank\' style=\'color:blue;\' href=\'https://fuc.loc\'>点击进入机场官网:富雨</a>。</b></p><p></p><p><h2>免费节点订阅链接:</h2></p><p></p><p><b>v2ray订阅链接:</b></p><p></p><p><a href=\'javascript:;\' data-clipboard-text=\'https://nodehub.loc/vfn/90f98a22914332c7/20260206-v2ray.txt\' class=\'copy-permalink\'></p><p><span class=\'subCopy\'><i class=\'iconfont icon-link\'></i> https://nodehub.loc/vfn/90f98a22914332c7/20260206-v2ray.txt</span></a></p><p></p><p><b>clash订阅链接:</b></p><p></p><p><a href=\'javascript:;\' data-clipboard-text=\'https://nodehub.loc/vfn/90f98a22914332c7/20260206-clash.yaml\' class=\'copy-permalink\'></p><p><span class=\'subCopy\'><i class=\'iconfont icon-link\'></i> https://nodehub.loc/vfn/90f98a22914332c7/20260206-clash.yaml</span></a></p><p></p><p>温馨提示:</p><p></p><p>本站提供的都是免费且公共的节点,稳定性与长时间连接速率无法与收费版的高速机场节点相提并论,不能奢望太多。</p><p></p><p>注意你自己的网络环境(本地连接当中的DNS,手动配置一下:114.114.114.114,8.8.8.8或是其它公共的DNS。)</p><p></p><p>如果某个网址无法代理访问,可切换到全局代理模式,一般可解决。</p><p></p><p></p>','https://resource.loc/upload/images/tg_xiuren_a-01KFAWNMJ27J8TDP1H0YPMNDP4-1-thumb_c.jpg',0,1,0,1,NULL,NULL,'2026-02-05 23:36:50','2026-02-06 00:38:54'),
|
||||
(35,1,1,'「2月7日」2026年最新高速SSR/V2ray/Clash/Shadowrocket免费节点订阅链接','本次更新共38个可用节点,最高速度21M/S。\n覆盖香港、台湾、爱尔兰、乌克兰、阿根廷、韩国、德国等多个区域。\n复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。','fn20260207','<p><b>加入tg频道获取更多新鲜免费节点:<a target=\"__blank\" style=\"color:blue;\" href=\"https://t.me/t.me/v2rayfreenode_com\">t.me/v2rayfreenode_com</a>。</b></p><p></p><p>本次更新共38个可用节点,最高速度21M/S。</p><p>覆盖香港、台湾、爱尔兰、乌克兰、阿根廷、韩国、德国等多个区域。</p><p>复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。</p><p></p><p><h2>机场推荐:</h2></p><p></p><p>* 好好 - 就是好</p><p></p><p>* 不好不好 - 就是不好</p><p></p><p>* 你就是个瘠薄 - 知道不</p><p></p><p>* 去死吧 - 去死</p><p></p><p><b><a target=\'__blank\' style=\'color:blue;\' href=\'https://fuc.loc\'>点击进入机场官网:富雨</a>。</b></p><p></p><p><h2>免费节点订阅链接:</h2></p><p></p><p><b>v2ray订阅链接:</b></p><p></p><p><a href=\'javascript:;\' data-clipboard-text=\'https://nodehub.loc/vfn/bdc9177b109b2339/20260207-v2ray.txt\' class=\'copy-permalink\'></p><p><span class=\'subCopy\'><i class=\'iconfont icon-link\'></i> https://nodehub.loc/vfn/bdc9177b109b2339/20260207-v2ray.txt</span></a></p><p></p><p><b>clash订阅链接:</b></p><p></p><p><a href=\'javascript:;\' data-clipboard-text=\'https://nodehub.loc/vfn/bdc9177b109b2339/20260207-clash.yaml\' class=\'copy-permalink\'></p><p><span class=\'subCopy\'><i class=\'iconfont icon-link\'></i> https://nodehub.loc/vfn/bdc9177b109b2339/20260207-clash.yaml</span></a></p><p></p><p>温馨提示:</p><p></p><p>本站提供的都是免费且公共的节点,稳定性与长时间连接速率无法与收费版的高速机场节点相提并论,不能奢望太多。</p><p></p><p>注意你自己的网络环境(本地连接当中的DNS,手动配置一下:114.114.114.114,8.8.8.8或是其它公共的DNS。)</p><p></p><p>如果某个网址无法代理访问,可切换到全局代理模式,一般可解决。</p><p></p><p></p>','https://resource.loc/upload/images/tg_xiuren_a-01KF5WX0N8VE3D773CFWZQBJVX-1-thumb_b.jpg',0,1,0,109,937,100010,'2026-02-07 00:09:20','2026-02-09 01:22:53'),
|
||||
(36,1,1,'「2月11日」2026年最新高速SSR/V2ray/Clash/Shadowrocket免费节点订阅链接','本次更新共37个可用节点,最高速度22M/S。\n覆盖台湾、越南、荷兰、冰岛、新加坡、台湾、土耳其、法国、加拿大等多个区域。\n复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。','fn20260211','<p><b>加入tg频道获取更多新鲜免费节点:<a target=\"__blank\" style=\"color:blue;\" href=\"https://t.me/v2rayfreenode_com\">t.me/v2rayfreenode_com</a>。</b></p><p></p><p>本次更新共37个可用节点,最高速度22M/S。</p><p>覆盖台湾、越南、荷兰、冰岛、新加坡、台湾、土耳其、法国、加拿大等多个区域。</p><p>复制下方的v2ray/Clash订阅链接,在客户端添加即可正常使用。</p><p></p><p><h2>机场推荐:</h2></p><p></p><p>* 好好 - 就是好</p><p></p><p>* 不好不好 - 就是不好</p><p></p><p>* 你就是个瘠薄 - 知道不</p><p></p><p>* 去死吧 - 去死</p><p></p><p><b><a target=\'__blank\' style=\'color:blue;\' href=\'https://fuc.loc\'>点击进入机场官网:富雨</a>。</b></p><p></p><p><h2>免费节点订阅链接:</h2></p><p></p><p><b>v2ray订阅链接:</b></p><p></p><p><a href=\'javascript:;\' data-clipboard-text=\'https://nodehub.loc/vfn/ff0acf9080acfa7e/20260211-v2ray.txt\' class=\'copy-permalink\'></p><p><span class=\'subCopy\'><i class=\'iconfont icon-link\'></i> https://nodehub.loc/vfn/ff0acf9080acfa7e/20260211-v2ray.txt</span></a></p><p></p><p><b>clash订阅链接:</b></p><p></p><p><a href=\'javascript:;\' data-clipboard-text=\'https://nodehub.loc/vfn/ff0acf9080acfa7e/20260211-clash.yaml\' class=\'copy-permalink\'></p><p><span class=\'subCopy\'><i class=\'iconfont icon-link\'></i> https://nodehub.loc/vfn/ff0acf9080acfa7e/20260211-clash.yaml</span></a></p><p></p><p>温馨提示:</p><p></p><p>本站提供的都是免费且公共的节点,稳定性与长时间连接速率无法与收费版的高速机场节点相提并论,不能奢望太多。</p><p></p><p>注意你自己的网络环境(本地连接当中的DNS,手动配置一下:114.114.114.114,8.8.8.8或是其它公共的DNS。)</p><p></p><p>如果某个网址无法代理访问,可切换到全局代理模式,一般可解决。</p><p></p><p></p>','https://resource.loc/upload/images/tg_xiuren_a-01KF5WX0N8VE3D773CFWZQBJVX-1-thumb_c.jpg',0,1,0,0,11,NULL,'2026-02-10 23:39:40','2026-02-10 23:59:48');
|
||||
/*!40000 ALTER TABLE `articles` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `cache`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `cache`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `cache` (
|
||||
`key` varchar(255) NOT NULL,
|
||||
`value` mediumtext NOT NULL,
|
||||
`expiration` int(11) NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `cache`
|
||||
--
|
||||
|
||||
LOCK TABLES `cache` WRITE;
|
||||
/*!40000 ALTER TABLE `cache` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `cache` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `cache_locks`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `cache_locks`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `cache_locks` (
|
||||
`key` varchar(255) NOT NULL,
|
||||
`owner` varchar(255) NOT NULL,
|
||||
`expiration` int(11) NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `cache_locks`
|
||||
--
|
||||
|
||||
LOCK TABLES `cache_locks` WRITE;
|
||||
/*!40000 ALTER TABLE `cache_locks` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `cache_locks` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `categories`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `categories`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `categories` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`iParentId` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '父级ID',
|
||||
`sName` varchar(50) NOT NULL COMMENT '分类名称',
|
||||
`sSlug` varchar(50) NOT NULL COMMENT 'URL标识',
|
||||
`sDescription` varchar(255) DEFAULT NULL COMMENT '描述',
|
||||
`iSort` int(11) NOT NULL DEFAULT 0 COMMENT '排序',
|
||||
`iStatus` tinyint(4) NOT NULL DEFAULT 1 COMMENT '状态:1显示 0隐藏',
|
||||
`sHtmlClass` varchar(50) DEFAULT NULL,
|
||||
`iShowChildren` tinyint(4) DEFAULT 0,
|
||||
`aBanner` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`aBanner`)),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `categories_sslug_unique` (`sSlug`),
|
||||
KEY `categories_istatus_index` (`iStatus`),
|
||||
KEY `categories_iparentid_index` (`iParentId`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `categories`
|
||||
--
|
||||
|
||||
LOCK TABLES `categories` WRITE;
|
||||
/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
|
||||
INSERT INTO `categories` VALUES
|
||||
(1,0,'免费节点','freenode','',0,1,'fas fa-wifi',0,NULL),
|
||||
(2,0,'客户端','client','',0,1,'fab fa-app-store-ios',1,NULL),
|
||||
(3,2,'windows','windows',NULL,0,1,NULL,0,'{\"img_file\":\"windows.jpg\", \"img_top\":\"24%\"}'),
|
||||
(4,2,'andorid','andorid',NULL,0,1,NULL,0,NULL);
|
||||
/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `dog_option`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `dog_option`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `dog_option` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`dog_name` varchar(255) DEFAULT NULL,
|
||||
`k` varchar(255) DEFAULT NULL,
|
||||
`v` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_this_name_k` (`dog_name`,`k`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `dog_option`
|
||||
--
|
||||
|
||||
LOCK TABLES `dog_option` WRITE;
|
||||
/*!40000 ALTER TABLE `dog_option` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `dog_option` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `failed_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `failed_jobs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `failed_jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(255) NOT NULL,
|
||||
`connection` text NOT NULL,
|
||||
`queue` text NOT NULL,
|
||||
`payload` longtext NOT NULL,
|
||||
`exception` longtext NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `failed_jobs`
|
||||
--
|
||||
|
||||
LOCK TABLES `failed_jobs` WRITE;
|
||||
/*!40000 ALTER TABLE `failed_jobs` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `failed_jobs` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `job_batches`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `job_batches`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `job_batches` (
|
||||
`id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`total_jobs` int(11) NOT NULL,
|
||||
`pending_jobs` int(11) NOT NULL,
|
||||
`failed_jobs` int(11) NOT NULL,
|
||||
`failed_job_ids` longtext NOT NULL,
|
||||
`options` mediumtext DEFAULT NULL,
|
||||
`cancelled_at` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`finished_at` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `job_batches`
|
||||
--
|
||||
|
||||
LOCK TABLES `job_batches` WRITE;
|
||||
/*!40000 ALTER TABLE `job_batches` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `job_batches` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `jobs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`queue` varchar(255) NOT NULL,
|
||||
`payload` longtext NOT NULL,
|
||||
`attempts` tinyint(3) unsigned NOT NULL,
|
||||
`reserved_at` int(10) unsigned DEFAULT NULL,
|
||||
`available_at` int(10) unsigned NOT NULL,
|
||||
`created_at` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `jobs_queue_index` (`queue`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `jobs`
|
||||
--
|
||||
|
||||
LOCK TABLES `jobs` WRITE;
|
||||
/*!40000 ALTER TABLE `jobs` DISABLE KEYS */;
|
||||
INSERT INTO `jobs` VALUES
|
||||
(1,'default','{\"uuid\":\"314518ec-b054-4d77-a356-1aa9ec26eaac\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769775319,\"delay\":null}',0,NULL,1769775319,1769775319),
|
||||
(2,'default','{\"uuid\":\"abc3fbce-24a0-4eaf-96c4-7fcaf00f95aa\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769776393,\"delay\":null}',0,NULL,1769776393,1769776393),
|
||||
(3,'default','{\"uuid\":\"7ad35a86-09cb-4d96-82c7-42750b0231bc\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769776737,\"delay\":null}',0,NULL,1769776737,1769776737),
|
||||
(4,'default','{\"uuid\":\"339cc071-8610-4382-9c5a-de4213562305\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769777044,\"delay\":null}',0,NULL,1769777044,1769777044),
|
||||
(5,'default','{\"uuid\":\"d424ace0-602a-4d8d-a2f8-4421f9a81aeb\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769777247,\"delay\":null}',0,NULL,1769777247,1769777247),
|
||||
(6,'default','{\"uuid\":\"6f098b90-ead8-4da3-87b9-59e253c03372\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769777583,\"delay\":null}',0,NULL,1769777583,1769777583),
|
||||
(7,'default','{\"uuid\":\"e16751ef-3835-477a-bcdd-4f58799a7e6e\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769777778,\"delay\":null}',0,NULL,1769777778,1769777778),
|
||||
(8,'default','{\"uuid\":\"d6494cf5-1d0b-47e2-ab4f-cc9a0a9a0f5c\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769777794,\"delay\":null}',0,NULL,1769777794,1769777794),
|
||||
(9,'default','{\"uuid\":\"c6de7ee4-7f70-4a55-a954-f7b3d9c2a187\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769778083,\"delay\":null}',0,NULL,1769778083,1769778083),
|
||||
(10,'default','{\"uuid\":\"219e91da-b167-4342-9135-3199d3d6731e\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769780368,\"delay\":null}',0,NULL,1769780368,1769780368),
|
||||
(11,'default','{\"uuid\":\"d59f8460-c8ae-4587-b651-b42f1cd4c592\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769780529,\"delay\":null}',0,NULL,1769780529,1769780529),
|
||||
(12,'default','{\"uuid\":\"901ccd34-c4cf-44f3-a770-265c71567ee7\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769780620,\"delay\":null}',0,NULL,1769780620,1769780620),
|
||||
(13,'default','{\"uuid\":\"4616bf6b-cc4f-4379-941c-88d99f89e8cf\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769832922,\"delay\":null}',0,NULL,1769832922,1769832922),
|
||||
(14,'default','{\"uuid\":\"28ee4b0b-2e02-4a73-9da2-c99180cca4ab\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833122,\"delay\":null}',0,NULL,1769833122,1769833122),
|
||||
(15,'default','{\"uuid\":\"1870c533-c149-498d-9b33-dc5dd75c9195\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833645,\"delay\":null}',0,NULL,1769833646,1769833646),
|
||||
(16,'default','{\"uuid\":\"9b079f8e-c05a-4f41-baa9-ae56908f2832\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833715,\"delay\":null}',0,NULL,1769833715,1769833715),
|
||||
(17,'default','{\"uuid\":\"e719def7-8635-4c24-b8c9-55dd64686001\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833825,\"delay\":null}',0,NULL,1769833825,1769833825),
|
||||
(18,'default','{\"uuid\":\"6e69c65a-6de9-4e29-ac49-dd0e62700493\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833876,\"delay\":null}',0,NULL,1769833876,1769833876),
|
||||
(19,'default','{\"uuid\":\"f6052246-365d-45e7-8124-963cc9a8e9cc\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833926,\"delay\":null}',0,NULL,1769833926,1769833926),
|
||||
(20,'default','{\"uuid\":\"7f0e95cd-0440-447e-b0ed-61deec6ef651\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769833959,\"delay\":null}',0,NULL,1769833959,1769833959),
|
||||
(21,'default','{\"uuid\":\"a6c5b6b2-5029-48ff-93f4-3334c8f33983\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769834017,\"delay\":null}',0,NULL,1769834017,1769834017),
|
||||
(22,'default','{\"uuid\":\"60f78b6b-3fc0-4a55-918d-d2a5db67211d\",\"displayName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"job\":\"Illuminate\\\\Queue\\\\CallQueuedHandler@call\",\"maxTries\":null,\"maxExceptions\":null,\"failOnTimeout\":false,\"backoff\":null,\"timeout\":null,\"retryUntil\":null,\"data\":{\"commandName\":\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\",\"command\":\"O:33:\\\"Laravel\\\\Scout\\\\Jobs\\\\MakeSearchable\\\":2:{s:6:\\\"models\\\";O:45:\\\"Illuminate\\\\Contracts\\\\Database\\\\ModelIdentifier\\\":5:{s:5:\\\"class\\\";s:19:\\\"App\\\\Models\\\\Articles\\\";s:2:\\\"id\\\";a:29:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:8;i:7;i:9;i:8;i:10;i:9;i:11;i:10;i:12;i:11;i:13;i:12;i:14;i:13;i:15;i:14;i:16;i:15;i:17;i:16;i:18;i:17;i:19;i:18;i:20;i:19;i:21;i:20;i:22;i:21;i:23;i:22;i:24;i:23;i:25;i:24;i:26;i:25;i:27;i:26;i:28;i:27;i:29;i:28;i:30;}s:9:\\\"relations\\\";a:0:{}s:10:\\\"connection\\\";s:5:\\\"mysql\\\";s:15:\\\"collectionClass\\\";N;}s:10:\\\"connection\\\";s:8:\\\"database\\\";}\"},\"createdAt\":1769834486,\"delay\":null}',0,NULL,1769834486,1769834486);
|
||||
/*!40000 ALTER TABLE `jobs` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `migrations`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `migrations`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `migrations` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`migration` varchar(255) NOT NULL,
|
||||
`batch` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `migrations`
|
||||
--
|
||||
|
||||
LOCK TABLES `migrations` WRITE;
|
||||
/*!40000 ALTER TABLE `migrations` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `migrations` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `option`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `option`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `option` (
|
||||
`k` varchar(255) NOT NULL,
|
||||
`v` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`k`),
|
||||
UNIQUE KEY `k` (`k`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `option`
|
||||
--
|
||||
|
||||
LOCK TABLES `option` WRITE;
|
||||
/*!40000 ALTER TABLE `option` DISABLE KEYS */;
|
||||
INSERT INTO `option` VALUES
|
||||
('dog_limit','30');
|
||||
/*!40000 ALTER TABLE `option` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `password_reset_tokens`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `password_reset_tokens`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `password_reset_tokens` (
|
||||
`email` varchar(255) NOT NULL,
|
||||
`token` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `password_reset_tokens`
|
||||
--
|
||||
|
||||
LOCK TABLES `password_reset_tokens` WRITE;
|
||||
/*!40000 ALTER TABLE `password_reset_tokens` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `password_reset_tokens` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `sessions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `sessions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `sessions` (
|
||||
`id` varchar(255) NOT NULL,
|
||||
`user_id` bigint(20) unsigned DEFAULT NULL,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`user_agent` text DEFAULT NULL,
|
||||
`payload` longtext NOT NULL,
|
||||
`last_activity` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `sessions_user_id_index` (`user_id`),
|
||||
KEY `sessions_last_activity_index` (`last_activity`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `sessions`
|
||||
--
|
||||
|
||||
LOCK TABLES `sessions` WRITE;
|
||||
/*!40000 ALTER TABLE `sessions` DISABLE KEYS */;
|
||||
INSERT INTO `sessions` VALUES
|
||||
('4wDk8oLxXxcc1ec81FFkzmEA7B1XnxwxAeibg2RA',NULL,'127.0.0.1','','YToyOntzOjY6Il90b2tlbiI7czo0MDoibTlsZnRTRUxnZ2FMNlZXUnZoRmZjQzZXUWNuOHdWRFZPQU9JWFp3USI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319fQ==',1770796010),
|
||||
('H6gerOjhSpvfaVoI850Wit0KWDKobktEs0pmTVUz',NULL,'127.0.0.1','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15','YTozOntzOjY6Il90b2tlbiI7czo0MDoib3dMWEl2Q0pSNEc0R05xTEdqQVlkTXUxRmVGM2NiUHpuQll2OXhnbCI7czo5OiJfcHJldmlvdXMiO2E6Mjp7czozOiJ1cmwiO3M6MjQ6Imh0dHA6Ly9mbi1iLmxvYy9mcmVlbm9kZSI7czo1OiJyb3V0ZSI7czoxMzoiYXJ0aWNsZXMubGlzdCI7fXM6NjoiX2ZsYXNoIjthOjI6e3M6Mzoib2xkIjthOjA6e31zOjM6Im5ldyI7YTowOnt9fX0=',1770804442),
|
||||
('qnewggVDrZqiqXZEXOQ9I5RbTBTsWf3RjEhkjm7F',NULL,'127.0.0.1','','YToyOntzOjY6Il90b2tlbiI7czo0MDoiWVRhTDFCWDdHWXR3bXFyWlBtenBGY0ZxWGNCZWxpcjFhYUtkR3VQeCI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319fQ==',1770795582),
|
||||
('r3May3X9tUPh7997YKw6Dm6GrRIFrWF57eF16xTr',NULL,'127.0.0.1','','YToyOntzOjY6Il90b2tlbiI7czo0MDoiOTAzZEVza3pOMHRsSTFaTUNBSU1MRXFZcUl2V1VtNTNQNFhWYWpBcSI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319fQ==',1770796533),
|
||||
('V6y6YVIMJpxRJb1FNFYLSEXBSs1SBLL5fMbwynb3',NULL,'127.0.0.1','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15','YTozOntzOjY6Il90b2tlbiI7czo0MDoiQlpZeHRIenZNbG13VmZIU2xOUDliS1dIVW9ZbGE4akdVc3NHbDVHSiI7czo5OiJfcHJldmlvdXMiO2E6Mjp7czozOiJ1cmwiO3M6MTU6Imh0dHA6Ly9mbi1iLmxvYyI7czo1OiJyb3V0ZSI7czoxMzoiYXJ0aWNsZXMuaG9tZSI7fXM6NjoiX2ZsYXNoIjthOjI6e3M6Mzoib2xkIjthOjA6e31zOjM6Im5ldyI7YTowOnt9fX0=',1770801403),
|
||||
('wTzOJU2pCja2CqJ9ItP3p4Lj5gTs5cSMbeyzZpUA',NULL,'127.0.0.1','','YToyOntzOjY6Il90b2tlbiI7czo0MDoiQXBkRmh4TlJoTzBJY0IxbUVyMjVnNjE1bE4xT3JwQVVRUUlMeG82QiI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319fQ==',1770796790);
|
||||
/*!40000 ALTER TABLE `sessions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `tags`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `tags`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `tags` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`sName` varchar(50) NOT NULL,
|
||||
`sSlug` varchar(50) NOT NULL,
|
||||
`iArticleCount` int(11) unsigned DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `slug_unique` (`sSlug`),
|
||||
KEY `iArticleCount` (`iArticleCount`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `tags`
|
||||
--
|
||||
|
||||
LOCK TABLES `tags` WRITE;
|
||||
/*!40000 ALTER TABLE `tags` DISABLE KEYS */;
|
||||
INSERT INTO `tags` VALUES
|
||||
(5,'Shadowrocket','shadowrocket-7vf7o',2),
|
||||
(6,'Quantumult','quantumult-Ggc90',2),
|
||||
(7,'Clash','clash-wd81q',2),
|
||||
(8,'SSR','ssr-BjeEq',2),
|
||||
(9,'节点','jie-dian-yIt5z',2),
|
||||
(10,'订阅链接','ding-yue-lian-jie-ruNF0',2),
|
||||
(11,'免费节点','mian-fei-jie-dian-hOpYo',2),
|
||||
(12,'v2ray','v2ray-Pd4HR',2);
|
||||
/*!40000 ALTER TABLE `tags` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`email` varchar(255) NOT NULL,
|
||||
`email_verified_at` timestamp NULL DEFAULT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`remember_token` varchar(100) DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `users_email_unique` (`email`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `users`
|
||||
--
|
||||
|
||||
LOCK TABLES `users` WRITE;
|
||||
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
||||
INSERT INTO `users` VALUES
|
||||
(1,'v2rayfreenode','v2rayfreenode',NULL,'111',NULL,NULL,NULL);
|
||||
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;
|
||||
|
||||
-- Dump completed on 2026-02-11 18:12:42
|
||||
40
app/Console/Commands/ClearDb.php
Executable file
40
app/Console/Commands/ClearDb.php
Executable file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\ArticleLikes;
|
||||
|
||||
class ClearDb extends Command
|
||||
{
|
||||
protected $signature = 'clear_db';
|
||||
protected $description = '清理过期数据';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->info("开始清理过期数据...");
|
||||
|
||||
$this->articleLikes();
|
||||
|
||||
$this->info("数据清理完成 !");
|
||||
}
|
||||
|
||||
private function articleLikes()
|
||||
{
|
||||
// 1. 确定 30 天前的时间点
|
||||
$oThresholdDate = now()->subDays(30);
|
||||
|
||||
// 2. 执行删除:直接在数据库层面一锅端
|
||||
// 药水哥提示:这样写只发一条 DELETE SQL,效率最高!
|
||||
$iDeletedCount = ArticleLikes::where('created_at', '<', $oThresholdDate)->delete();
|
||||
|
||||
// 3. 给点反馈,这叫“开发者的温柔”
|
||||
if ($iDeletedCount > 0) {
|
||||
$this->warn("已成功清理 {$iDeletedCount} 条 30 天前的点赞记录!");
|
||||
} else {
|
||||
$this->info("点赞表很干净,没有过期数据。");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
71
app/Console/Commands/StatOrderLikes_meiyong.php
Executable file
71
app/Console/Commands/StatOrderLikes_meiyong.php
Executable file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use App\Models\ArticleLikes;
|
||||
|
||||
class StatOrderLikes extends Command
|
||||
{
|
||||
protected $signature = 'stat:order-likes';
|
||||
protected $description = '每周校准标签统计并更新视图组件';
|
||||
|
||||
// public function handle(): void
|
||||
// {
|
||||
// $this->info("order-likes开始渲染...");
|
||||
//
|
||||
// $aOrderLikes = ArticleLikes::with(['oArticle' => function($query) {
|
||||
// $query->where('iStatus', 1); // 只拿发布的,草稿别出来丢人
|
||||
// }])
|
||||
// ->orderBy('created_at', 'desc')
|
||||
// ->limit(10) // 拿最近的 10 条点赞记录
|
||||
// ->get();
|
||||
//
|
||||
// $sHtml = View::make('components.order-likes', ['oOrderLikes' => $aOrderLikes])->render();
|
||||
//
|
||||
// $sFilePath = resource_path('views/components/generated-order-likes.blade.php');
|
||||
//
|
||||
// File::put($sFilePath, "\n" . $sHtml);
|
||||
//
|
||||
// $this->info("渲染完成 !");
|
||||
// }
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->info("开始统计最近 7 天点赞排名并渲染...");
|
||||
|
||||
// 1. 获取 7 天前的时间点
|
||||
$oStartDate = now()->subDays(30);
|
||||
|
||||
// 2. 高标准聚合查询
|
||||
$aOrderLikes = ArticleLikes::select('iArticleId', \DB::raw('count(*) as iTotalLikes'))
|
||||
->where('created_at', '>=', $oStartDate) // 只要最近 7 天的
|
||||
->groupBy('iArticleId')
|
||||
->orderBy('iTotalLikes', 'desc') // 按点赞总数排
|
||||
->with(['oArticle' => function($query) {
|
||||
$query->where('iStatus', 1); // 同样,草稿文章爬远点
|
||||
}])
|
||||
->limit(10)
|
||||
->get()
|
||||
->filter(function($oItem) {
|
||||
return $oItem->oArticle !== null; // 过滤掉万一被删了的文章
|
||||
});
|
||||
|
||||
// 3. 渲染 HTML
|
||||
// 药水哥提示:这里的变量名我改成了更加语义化的 aHotArticles
|
||||
$sHtml = View::make('components.order-likes', ['oOrderLikes' => $aOrderLikes])->render();
|
||||
|
||||
// 4. 写入文件(保持你的风格)
|
||||
$sFilePath = resource_path('views/components/generated-order-likes.blade.php');
|
||||
|
||||
// 增加一行注释,方便后期 debug 知道这玩意儿是什么时候生成的
|
||||
$sComment = "<?php /* Generated at: " . now()->toDateTimeString() . " */ ?>\n";
|
||||
File::put($sFilePath, $sComment . $sHtml);
|
||||
|
||||
$this->info("7天点赞排名渲染完成 !");
|
||||
}
|
||||
|
||||
}
|
||||
63
app/Console/Commands/StatTagsCloud.php
Executable file
63
app/Console/Commands/StatTagsCloud.php
Executable file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class StatTagsCloud extends Command
|
||||
{
|
||||
protected $signature = 'stat:tags-cloud';
|
||||
protected $description = '每周校准标签统计并更新视图组件';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->info("tags-cloud开始渲染...");
|
||||
|
||||
DB::transaction(function () {
|
||||
// 1. 使用 JOIN 方式批量更新,iArticleCount 默认为 0
|
||||
// 这种写法比子查询更符合数据库执行计划优化
|
||||
DB::table('tags')->update(['iArticleCount' => 0]);
|
||||
|
||||
DB::statement("
|
||||
UPDATE tags t
|
||||
INNER JOIN (
|
||||
SELECT iTagId, COUNT(*) as iTotal
|
||||
FROM article_tag
|
||||
GROUP BY iTagId
|
||||
) at ON t.id = at.iTagId
|
||||
SET t.iArticleCount = at.iTotal
|
||||
");
|
||||
});
|
||||
|
||||
// 2. 渲染视图并持久化
|
||||
$this->renderToComponent();
|
||||
|
||||
$this->info("渲染完成!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 高标准:将统计结果预渲染,减少运行时查询
|
||||
*/
|
||||
private function renderToComponent(): void
|
||||
{
|
||||
// a 前缀,获取统计后的标签列表
|
||||
$aTags = DB::table('tags')
|
||||
->where('iArticleCount', '>', 0)
|
||||
->orderByDesc('iArticleCount')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
// 渲染 Blade 模板内容
|
||||
// 假设你的模板在 resources/views/components/tags-cloud-content.blade.php
|
||||
$sHtml = View::make('components.tags-cloud', ['aTags' => $aTags])->render();
|
||||
|
||||
// 将渲染好的 HTML 写入 resources 目录下的一个静态片段中
|
||||
// 这样组件调用时直接 @include,性能起飞
|
||||
$sFilePath = resource_path('views/components/generated-tags-cloud.blade.php');
|
||||
|
||||
File::put($sFilePath, "\n" . $sHtml);
|
||||
}
|
||||
}
|
||||
78
app/Console/Commands/SyncArticleLikes.php
Executable file
78
app/Console/Commands/SyncArticleLikes.php
Executable file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Articles;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class SyncArticleLikes extends Command
|
||||
{
|
||||
// 在 Kernel.php 中建议设置 ->everyFiveMinutes()
|
||||
protected $signature = 'article:sync-likes';
|
||||
protected $description = '高标准点赞:7天自动碎化 + 全年天级随机';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$iBurstCount = 0; // 记录本次执行爆发了多少文章
|
||||
$iYearlyCount = 0; // 记录本次执行有多少文章中了全年奖
|
||||
|
||||
// 1. 【7天爆发】碎化注入逻辑
|
||||
Articles::where('i7like', '>', 0)
|
||||
->where('iStatus', 1)
|
||||
->chunkById(100, function ($aArticles) use (&$iBurstCount) {
|
||||
foreach ($aArticles as $oArticle) {
|
||||
$iDaysPassed = $oArticle->created_at->diffInDays(now());
|
||||
if ($iDaysPassed > 6) continue;
|
||||
|
||||
$iRemainingSlots = (7 - $iDaysPassed) * 24 * 12;
|
||||
if ($iRemainingSlots <= 0) continue;
|
||||
|
||||
$iBaseAdd = (int)ceil($oArticle->i7like / $iRemainingSlots);
|
||||
|
||||
if (mt_rand(1, 100) <= 50) {
|
||||
$iAdd = mt_rand(1, max(1, $iBaseAdd * 2));
|
||||
$this->transferLikes($oArticle, 'i7like', $iAdd);
|
||||
|
||||
$this->info("ID [{$oArticle->id}] 爆发:注入 {$iAdd} 个赞,剩余库存 {$oArticle->i7like}");
|
||||
$iBurstCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 【全年增长】天级随机逻辑
|
||||
$sLockKey = 'sync_yearly_likes_lock:' . now()->format('Ymd');
|
||||
if (!Cache::has($sLockKey)) {
|
||||
$this->warn("检测到今日尚未进行全年随机摇号,开始抽奖...");
|
||||
|
||||
Articles::where('i365like', '>', 0)
|
||||
->where('iStatus', 1)
|
||||
->chunkById(100, function ($aArticles) use (&$iYearlyCount) {
|
||||
foreach ($aArticles as $oArticle) {
|
||||
if (mt_rand(1, 365) <= $oArticle->i365like) {
|
||||
$oArticle->increment('iLikeCount');
|
||||
$this->line("ID [{$oArticle->id}] 全年:运气爆发,喜提 1 个赞");
|
||||
$iYearlyCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Cache::put($sLockKey, 1, now()->addDay());
|
||||
}
|
||||
|
||||
$this->comment("本次任务结束:{$iBurstCount} 篇爆发增长,{$iYearlyCount} 篇全年喜提。");
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心转移逻辑
|
||||
*/
|
||||
private function transferLikes(Articles $oArticle, string $sField, int $iAmount): void
|
||||
{
|
||||
$iRealAdd = min($oArticle->$sField, $iAmount);
|
||||
if ($iRealAdd > 0) {
|
||||
$oArticle->increment('iLikeCount', $iRealAdd);
|
||||
// 这里注意:increment/decrement 会同步更新 model 对象的内存值
|
||||
$oArticle->decrement($sField, $iRealAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
app/Http/Controllers/Api/CacheController.php
Executable file
28
app/Http/Controllers/Api/CacheController.php
Executable file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
//use App\Models\Articles as ModelArticles;
|
||||
use Spatie\ResponseCache\Facades\ResponseCache;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Articles as ModelArticles;
|
||||
|
||||
class CacheController extends Controller
|
||||
{
|
||||
public function clear()
|
||||
{
|
||||
ResponseCache::clear();
|
||||
echo '缓存清除完毕';
|
||||
exit;
|
||||
}
|
||||
|
||||
public function test1()
|
||||
{
|
||||
$o = ModelArticles::find(1);
|
||||
$o->sTitle = time();
|
||||
$o->save();
|
||||
|
||||
tt($o->toArray());
|
||||
}
|
||||
}
|
||||
79
app/Http/Controllers/Api/v1/ArticlesController.php
Executable file
79
app/Http/Controllers/Api/v1/ArticlesController.php
Executable file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\v1;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Services\TomTool\Telegram\Slave as TeleSlave;
|
||||
use App\Models\Articles;
|
||||
use App\Models\Categories;
|
||||
use App\Models\ArticleFreenodeThumbNum;
|
||||
use App\Services\TomTool\HttpV2;
|
||||
use App\Services\TagService;
|
||||
|
||||
class ArticlesController extends Controller
|
||||
{
|
||||
public function receiveFreenode(Request $oRequest)
|
||||
{
|
||||
|
||||
$sObjName = "nodehub:article_receive_freenode";
|
||||
$oFlow = Flow::make($sObjName);
|
||||
|
||||
$aData = $oRequest->all();
|
||||
$aArticle = $aData["article"];
|
||||
$sArticleTitle = $aArticle["title"];
|
||||
$sArticleTitleSub = $aArticle["title_sub"];
|
||||
$sArticleContent = $aArticle["content"];
|
||||
$aArticleTags = $aArticle["tags"];
|
||||
$sSlug = 'fn'.date("Ymd");
|
||||
$sImgAuthor = "tg_xiuren_a";
|
||||
$i7like = $aArticle["7like"];
|
||||
|
||||
$oThumbNum = ArticleFreenodeThumbNum::firstOrCreate(["author_code" => $sImgAuthor]);
|
||||
$iThumbNum = $oThumbNum->num;
|
||||
|
||||
$sImagesApi = "https://".config("path.url_resource_base")."/api/images/get/url-by-num";
|
||||
|
||||
$aApiArg = [
|
||||
"sAuthorCode" => $sImgAuthor,
|
||||
"iNum" => $iThumbNum+1,
|
||||
];
|
||||
$sApiResult = HttpV2::make($sImagesApi, "post")->withToken()->setDataA($aApiArg)->send();
|
||||
$aApiResult = json_decode($sApiResult, true);
|
||||
|
||||
if (!isset($aApiResult["isDone"]) || !$aApiResult["isDone"]) {
|
||||
echo $sApiResult;
|
||||
return response()->json($oFlow->fail("{$sObjName}的图片api调用失败"));
|
||||
}
|
||||
|
||||
$sThumbPath = $aApiResult["aData"]["sFileUrl"];
|
||||
$sThumbNumTrue = $aApiResult["aData"]["iTrueNum"];
|
||||
|
||||
$oCategories = Categories::where("sSlug", "freenode")->first();
|
||||
|
||||
$oArticle = Articles::updateOrCreate(
|
||||
['sSlug' => $sSlug],
|
||||
[
|
||||
'sThumb' => $sThumbPath,
|
||||
'iCategoryId' => $oCategories->id,
|
||||
'sTitle' => $sArticleTitle,
|
||||
'sTitleSub' => $sArticleTitleSub,
|
||||
'sContent' => $sArticleContent,
|
||||
'i7like' => $i7like,
|
||||
'iStatus' => 1,
|
||||
'iUserId' => 1,
|
||||
],
|
||||
);
|
||||
|
||||
(new TagService())->syncArticleTagsByName($oArticle, $aArticleTags);
|
||||
|
||||
ArticleFreenodeThumbNum::where("author_code", $sImgAuthor)->update(["num" => $sThumbNumTrue]);
|
||||
|
||||
$sArticleUrl = config("app.url")."/feenode/".$sSlug;
|
||||
|
||||
TeleSlave::notify()->send($sArticleUrl." 发布成功");
|
||||
|
||||
return response()->json($oFlow->done());
|
||||
}
|
||||
}
|
||||
197
app/Http/Controllers/Api/v1/Telegram/HookController.php
Executable file
197
app/Http/Controllers/Api/v1/Telegram/HookController.php
Executable file
@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\v1\Telegram;
|
||||
|
||||
//use App\Models\Article;
|
||||
use App\Services\TomTool\Http;
|
||||
use App\Services\Tg\Http as TGHttp;
|
||||
use App\Models\Hook as ModelHook;
|
||||
use App\Services\TomTool\ThrowableHandler;
|
||||
//use App\Services\TomTool\TelegramVdb;
|
||||
use App\Services\TomTool\Telegram\Slave as TelegramSlave;
|
||||
|
||||
final class HookController
|
||||
{
|
||||
private $sCustomText = '';
|
||||
|
||||
public function cache_tg($sGroupCode)
|
||||
{
|
||||
try {
|
||||
|
||||
$sUpdate = file_get_contents("php://input");
|
||||
$aUpdate = json_decode($sUpdate, true);
|
||||
$sText = $aUpdate["message"]["text"] ?? "没有text";
|
||||
if (!str_starts_with($sText, '/')) {
|
||||
$this->sCustomText = "/cacheTg#add__{$sGroupCode}"." ".$sUpdate;
|
||||
}
|
||||
|
||||
$this->cmd();
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
$aDataTg = ThrowableHandler::make($e)->enableTrace()->fetch();
|
||||
TelegramSlave::fail()->enableSlaveQueue(false)->setMsgA([
|
||||
"msg" => $aDataTg,
|
||||
"data" => $sUpdate,
|
||||
])->send();
|
||||
|
||||
$aDataCmd = $aDataTg;
|
||||
$sDataCmd = json_encode($aDataCmd, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$sDataCmd = str_replace("\\n", "<br>", $sDataCmd);
|
||||
echo $sDataCmd;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function cmd_ffq_article()
|
||||
{
|
||||
try {
|
||||
|
||||
$sUpdate = file_get_contents("php://input");
|
||||
$aUpdate = json_decode($sUpdate, true);
|
||||
$sText = $aUpdate["message"]["text"] ?? "没有text";
|
||||
if (!str_starts_with($sText, '/')) {
|
||||
// $this->sCustomCmd = "article";
|
||||
// $this->sCustomFunc = "cache";
|
||||
// $this->aCustomOption[1] = "ffq";
|
||||
$this->sCustomText = "/article#cacheAdd__ffq_tg_a"." ".$sUpdate;
|
||||
}
|
||||
|
||||
$this->cmd();
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
$aDataTg = ThrowableHandler::make($e)->enableTrace()->fetch();
|
||||
TelegramSlave::fail()->enableSlaveQueue(false)->setMsgA([
|
||||
"msg" => $aDataTg,
|
||||
"data" => $sUpdate,
|
||||
])->send();
|
||||
|
||||
$aDataCmd = $aDataTg;
|
||||
$sDataCmd = json_encode($aDataCmd, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$sDataCmd = str_replace("\\n", "<br>", $sDataCmd);
|
||||
echo $sDataCmd;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function cmd()
|
||||
{
|
||||
|
||||
try {
|
||||
|
||||
$oTGHttp = new TGHttp();
|
||||
|
||||
$oTGHttp->sApiToken = env('telegram_token');
|
||||
$oTGHttp->sApiChatId = env('telegram_chatid');
|
||||
|
||||
$sUpdate = file_get_contents("php://input");
|
||||
|
||||
$aUpdate = json_decode($sUpdate, true);
|
||||
// @file_put_contents('/www/_tg/bot_log.txt', json_encode($aUpdate, JSON_PRETTY_PRINT));
|
||||
|
||||
$aData['chatId'] = (string) $aUpdate["message"]["chat"]["id"];
|
||||
$aData['fromId'] = (string) $aUpdate["message"]["from"]["id"];
|
||||
|
||||
if ($this->sCustomText) {
|
||||
$aUpdate["message"]["text"] = $this->sCustomText;
|
||||
}
|
||||
|
||||
if (!isset($aUpdate["message"]["text"])) {
|
||||
throw new \Exception(json_encode($aUpdate));
|
||||
}
|
||||
|
||||
$sText = $aUpdate["message"]["text"];
|
||||
|
||||
// hook优先
|
||||
// 非is_hook,并且,首字符非/
|
||||
if ((!isset($aUpdate["is_hook"]) || $aUpdate["is_hook"] == false) && substr($sText, 0, 1) !== "/") {
|
||||
|
||||
$sHookKey = ModelHook::where("tg_user_id", $aData["fromId"])->first()?->key;
|
||||
|
||||
if ($sHookKey) {
|
||||
if ($sText == "run") {
|
||||
$sText = $sHookKey;
|
||||
} else {
|
||||
$sText = $sHookKey.$sText;
|
||||
}
|
||||
$aUpdate["is_hook"] = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@$aArg = explode(" ", $sText) ?? [];
|
||||
@$aOption = explode("__", $aArg[0]) ?? [];
|
||||
|
||||
$sCmd = $this->cleanCmd($aOption[0]);
|
||||
|
||||
// 大于3个说明是hook或dove之类
|
||||
$aCmd = explode("#", $sCmd);
|
||||
// if (count($aCmd) >= 3) {
|
||||
// $sHook = $aCmd[0];
|
||||
// $sClass = $sHook;
|
||||
// $sFunc = "go";
|
||||
// } else {
|
||||
$sClass = $aCmd[0] ?? false;
|
||||
$sFunc = $aCmd[1] ?? false;
|
||||
// }
|
||||
|
||||
if (strpos($sClass, "dove$") !== false) {
|
||||
$sClass = "dove";
|
||||
$sFunc = "go";
|
||||
}
|
||||
|
||||
$sUClass = ucfirst($sClass);
|
||||
$sUFunc = ucfirst($sFunc);
|
||||
|
||||
unset($aOption[0]);
|
||||
unset($aArg[0]);
|
||||
|
||||
$aParams = [];
|
||||
$aParams['aOption'] = $aOption;
|
||||
$aParams['aArg'] = $aArg;
|
||||
|
||||
$sClassPath = "App\\Services\\Tg\\Hook\\Mod\\".$sClass;
|
||||
$sUClassPath = "App\\Services\\Tg\\Hook\\Mod\\".$sUClass;
|
||||
|
||||
$oInstans = new $sUClassPath($aUpdate);
|
||||
$r = $oInstans->$sFunc($aParams, $aUpdate);
|
||||
|
||||
$oTGHttp->send($r, $aUpdate);
|
||||
|
||||
echo $r;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
$aDataTg = ThrowableHandler::make($e)->enableTrace()->fetch();
|
||||
TelegramSlave::fail()->enableSlaveQueue(false)->setMsgA($aDataTg)->send();
|
||||
|
||||
$aDataCmd = $aDataTg;
|
||||
$aDataCmd["_update"] = $aUpdate;
|
||||
$sDataCmd = json_encode($aDataCmd, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$sDataCmd = str_replace("\\n", "<br>", $sDataCmd);
|
||||
echo $sDataCmd;
|
||||
exit;
|
||||
|
||||
// old
|
||||
// $sMessage = "错误(文件: " . $e->getFile() . ",行: " . $e->getLine() . "): " . $e->getMessage();
|
||||
// $oTGHttp->send($sMessage, $sUpdate);
|
||||
// //Http::response(200, 'error'); 注释的
|
||||
// old end
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanCmd($input) {
|
||||
|
||||
$input = str_replace('\\', '', $input);
|
||||
$input = str_replace('/', '', $input);
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
8
app/Http/Controllers/Controller.php
Executable file
8
app/Http/Controllers/Controller.php
Executable file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
|
||||
}
|
||||
13
app/Http/Controllers/TestController.php
Executable file
13
app/Http/Controllers/TestController.php
Executable file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TestController extends Controller
|
||||
{
|
||||
public function hello()
|
||||
{
|
||||
tt("hello");
|
||||
}
|
||||
}
|
||||
75
app/Http/Controllers/Web/ArticlesController.php
Executable file
75
app/Http/Controllers/Web/ArticlesController.php
Executable file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ArticlesService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Models\Articles;
|
||||
|
||||
class ArticlesController extends Controller
|
||||
{
|
||||
protected $oArticlesService;
|
||||
|
||||
public function __construct(ArticlesService $oService)
|
||||
{
|
||||
$this->oArticlesService = $oService;
|
||||
}
|
||||
|
||||
public function search(Request $oRequest, string $sKeyword)
|
||||
{
|
||||
$xData = $this->oArticlesService->search($sKeyword, 'view');
|
||||
return view('pages.articles.default.index', $xData);
|
||||
}
|
||||
|
||||
public function like(int $iArticleId, Request $oRequest)
|
||||
{
|
||||
// 1. 获取/生成指纹
|
||||
$sFingerprint = $oRequest->cookie('visitor_token')
|
||||
?? md5($oRequest->ip() . $oRequest->userAgent());
|
||||
|
||||
// 2. 调用 Service
|
||||
$aResult = $this->oArticlesService->like($iArticleId, $oRequest->ip(), $sFingerprint);
|
||||
|
||||
if (!$aResult['status']) {
|
||||
return response()->json($aResult);
|
||||
}
|
||||
|
||||
// 3. 成功后下发持久 Cookie
|
||||
return response()->json($aResult)
|
||||
->withCookie(cookie()->forever('visitor_token', $sFingerprint));
|
||||
}
|
||||
|
||||
public function listByTag(Request $oRequest, string $sSlug)
|
||||
{
|
||||
$xData = $this->oArticlesService->getTagArticles($sSlug, 'view');
|
||||
return view('pages.articles.default.index', $xData);
|
||||
}
|
||||
|
||||
public function list(Request $oRequest, string $sSlug1, string $sSlug2 = null)
|
||||
{
|
||||
$sSlug = $sSlug2 ?? $sSlug1;
|
||||
$xData = $this->oArticlesService->getCategoryArticles($sSlug, 'view');
|
||||
return view('pages.articles.default.index', $xData);
|
||||
}
|
||||
|
||||
public function show(string $sArticleSlug, Request $oRequest)
|
||||
{
|
||||
$sFingerprint = $oRequest->cookie('visitor_token')
|
||||
?? md5($oRequest->ip() . $oRequest->userAgent());
|
||||
|
||||
$xData = $this->oArticlesService->getArticle($sArticleSlug, $oRequest->ip(), $sFingerprint);
|
||||
|
||||
// $xData["siteUrl"] = config("app.url");
|
||||
|
||||
$sViewPath = 'pages.articles.default.show';
|
||||
//
|
||||
// if ($xData["oArticle"]->oCategory->sSlug == "freenode") {
|
||||
// $sViewPath = 'pages.articles.freenode.show';
|
||||
// }
|
||||
|
||||
return view($sViewPath, $xData);
|
||||
}
|
||||
}
|
||||
|
||||
26
app/Http/Controllers/Web/TagsController.php
Executable file
26
app/Http/Controllers/Web/TagsController.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Models\Tags;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class TagsController extends Controller
|
||||
{
|
||||
public function show(Tags $oTags): View
|
||||
{
|
||||
// 高标准:关联查询 + 分页
|
||||
// 假设你的关联关系叫 articles()
|
||||
// $aArticles = $oTags->articles()
|
||||
// ->with(['category', 'user']) // 预加载作者和分类,优雅!
|
||||
// ->where('iStatus', 1) // 只看发布状态的
|
||||
// ->orderByDesc('created_at')
|
||||
// ->paginate(15);
|
||||
//
|
||||
// return view('tag.show', [
|
||||
// 'oTag' => $oTag,
|
||||
// 'aArticles' => $aArticles
|
||||
// ]);
|
||||
}
|
||||
}
|
||||
25
app/Http/Middleware/ApiCheckToken.php
Executable file
25
app/Http/Middleware/ApiCheckToken.php
Executable file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ApiCheckToken
|
||||
{
|
||||
|
||||
public function handle($oRequest, Closure $next)
|
||||
{
|
||||
// 获取 Token
|
||||
$sIncomingToken = $oRequest->bearerToken();
|
||||
$sLocalToken = (string) env('token_base', false);
|
||||
|
||||
// 严丝合缝的校验
|
||||
if (!$sIncomingToken || $sIncomingToken !== $sLocalToken) {
|
||||
return response()->json(['msg' => 'token error'], 401);
|
||||
}
|
||||
|
||||
return $next($oRequest);
|
||||
}
|
||||
}
|
||||
12
app/Models/ArticleFreenodeThumbNum.php
Executable file
12
app/Models/ArticleFreenodeThumbNum.php
Executable file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ArticleFreenodeThumbNum extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
||||
23
app/Models/ArticleLikes.php
Executable file
23
app/Models/ArticleLikes.php
Executable file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\Articles;
|
||||
//use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
//use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class ArticleLikes extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
// public $timestamps = false;
|
||||
// protected $casts = [
|
||||
// 'aBanner' => 'array',
|
||||
// ];
|
||||
|
||||
public function oArticle()
|
||||
{
|
||||
return $this->belongsTo(Articles::class, 'iArticleId', 'id');
|
||||
}
|
||||
|
||||
}
|
||||
113
app/Models/Articles.php
Executable file
113
app/Models/Articles.php
Executable file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\ResponseCache\Facades\ResponseCache;
|
||||
use App\Models\Tags;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
|
||||
class Articles extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $appends = ['s_created_at_diff'];
|
||||
|
||||
use Searchable;
|
||||
|
||||
protected function sCreatedAtDiff(): Attribute
|
||||
{
|
||||
return Attribute::get(function () {
|
||||
// 1. 别直接用 $this->created_at,防止它还没被实例化成 Carbon 对象
|
||||
$oDate = \Carbon\Carbon::parse($this->created_at);
|
||||
$oNow = \Carbon\Carbon::now();
|
||||
|
||||
// 2. 计算小时差(强制转整数)
|
||||
$iHours = (int)$oDate->diffInHours($oNow);
|
||||
|
||||
if ($iHours < 1) {
|
||||
// 不足 1 小时,看看是不是不足 1 分钟
|
||||
$iMinutes = (int)$oDate->diffInMinutes($oNow);
|
||||
return $iMinutes < 1 ? '刚刚' : $iMinutes . '分钟前';
|
||||
}
|
||||
|
||||
if ($iHours < 24) {
|
||||
return $iHours . '小时前';
|
||||
}
|
||||
|
||||
// 3. 计算天数差(强制转整数)
|
||||
$iDays = (int)$oDate->diffInDays($oNow);
|
||||
if ($iDays < 30) {
|
||||
return $iDays . '天前';
|
||||
}
|
||||
|
||||
// 超过一个月直接显示日期
|
||||
return $oDate->format('Y-m-d');
|
||||
});
|
||||
}
|
||||
|
||||
public function shouldBeSearchable(): bool
|
||||
{
|
||||
return (int)$this->iStatus === 1;
|
||||
}
|
||||
|
||||
public function searchableAs(): string
|
||||
{
|
||||
return 'articles';
|
||||
}
|
||||
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
// 药水哥原则:字段要精准,类型要死磕
|
||||
return [
|
||||
'id' => (int) $this->id,
|
||||
'iCategoryId' => (int) $this->iCategoryId,
|
||||
'sCategoryName' => (string) ($this->oCategory->sName ?? 'none'),
|
||||
'sCategorySlug' => (string) ($this->oCategory->sSlug ?? 'none'),
|
||||
'sTitle' => (string) $this->sTitle,
|
||||
'sTitleSub' => (string) $this->sTitleSub,
|
||||
'sSlug' => (string) $this->sSlug,
|
||||
'sContent' => (string) mb_substr(strip_tags($this->sContent), 0, 500),
|
||||
'iStatus' => (int) $this->iStatus,
|
||||
'iIsTop' => (int) $this->iIsTop,
|
||||
'updated_at' => (int) $this->updated_at->timestamp,
|
||||
];
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::saved(function () {
|
||||
ResponseCache::forget('/'); // 要改成具体文章
|
||||
});
|
||||
|
||||
static::deleted(function () {
|
||||
ResponseCache::forget('/'); // 要改成具体文章
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function oTags(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
Tags::class,
|
||||
'article_tag',
|
||||
'iArticleId',
|
||||
'iTagId'
|
||||
);
|
||||
}
|
||||
|
||||
public function oCategory(): BelongsTo {
|
||||
return $this->belongsTo(Categories::class, 'iCategoryId', 'id');
|
||||
}
|
||||
|
||||
public function oUser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'iUserId', 'id');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
49
app/Models/Categories.php
Executable file
49
app/Models/Categories.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Categories extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
public $timestamps = false;
|
||||
protected $casts = [
|
||||
'aBanner' => 'array',
|
||||
];
|
||||
|
||||
protected $touches = ['oArticles'];
|
||||
|
||||
public function aArticles(): HasMany {
|
||||
return $this->hasMany(Articles::class, 'iCategoryId', 'id');
|
||||
}
|
||||
|
||||
public function aChildren()
|
||||
{
|
||||
return $this->hasMany(Categories::class, 'iParentId', 'id')->orderBy('iSort', 'desc');
|
||||
}
|
||||
|
||||
public function oParent()
|
||||
{
|
||||
return $this->belongsTo(Categories::class, 'iParentId', 'id');
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::saved(fn() => Cache::forget('global_nav_tree'));
|
||||
static::deleted(fn() => Cache::forget('global_nav_tree')); // 删除时也要清,这才叫严谨!
|
||||
}
|
||||
|
||||
public static function getGlobalNavTree()
|
||||
{
|
||||
return self::where('iParentId', 0)
|
||||
->where('iStatus', 1)
|
||||
->with(['aChildren' => fn($q) => $q->where('iStatus', 1)->orderBy('iSort', 'desc')])
|
||||
->orderBy('iSort', 'desc')
|
||||
->get();
|
||||
}
|
||||
|
||||
}
|
||||
43
app/Models/DogOption.php
Executable file
43
app/Models/DogOption.php
Executable file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DogOption extends Model
|
||||
{
|
||||
protected $table = 'dog_option';
|
||||
public $timestamps = false;
|
||||
|
||||
public static function _hit($sDogName, $k)
|
||||
{
|
||||
$v = self::where("dog_name", $sDogName)->where("k", $k)->first()?->v;
|
||||
return $v;
|
||||
}
|
||||
|
||||
public static function _list($sDogName)
|
||||
{
|
||||
$oSelf = self::where("dog_name", $sDogName)->get();
|
||||
|
||||
return self::_filterList($oSelf);
|
||||
}
|
||||
|
||||
public static function _filter($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
$arr[$oOption->dog_name."#".$oOption->k] = $oOption->v;
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function _filterList($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
foreach ($oOption as $oOptionRow) {
|
||||
$arr[] = self::_filter($oOptionRow);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
}
|
||||
47
app/Models/Option.php
Executable file
47
app/Models/Option.php
Executable file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Option extends Model
|
||||
{
|
||||
protected $table = 'option';
|
||||
public $timestamps = false;
|
||||
protected $primaryKey = 'k';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
|
||||
public static function _hit($k)
|
||||
{
|
||||
$v = self::where("k", $k)->first()?->v;
|
||||
|
||||
return $v;
|
||||
}
|
||||
|
||||
public static function _list()
|
||||
{
|
||||
$oSelf = self::get();
|
||||
|
||||
return self::_filterList($oSelf);
|
||||
}
|
||||
|
||||
public static function _filter($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
$arr[$oOption->k] = $oOption->v;
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function _filterList($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
foreach ($oOption as $oOptionRow) {
|
||||
$arr[] = self::_filter($oOptionRow);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
}
|
||||
26
app/Models/Tags.php
Executable file
26
app/Models/Tags.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
//use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
//use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
//use Spatie\ResponseCache\Facades\ResponseCache;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Tags extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
public $timestamps = false;
|
||||
|
||||
public function oArticles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
Articles::class, // 关联的文章模型
|
||||
'article_tag', // 中间表名
|
||||
'iTagId', // 中间表中指向当前表(tags)的外键
|
||||
'iArticleId' // 中间表中指向目标表(articles)的外键
|
||||
);
|
||||
}
|
||||
}
|
||||
54
app/Models/User.php
Executable file
54
app/Models/User.php
Executable file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
public function aArticles(): HasMany
|
||||
{
|
||||
return $this->hasMany(Articles::class, 'iUserId', 'id');
|
||||
}
|
||||
}
|
||||
28
app/Providers/AppServiceProvider.php
Executable file
28
app/Providers/AppServiceProvider.php
Executable file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Relation::morphMap([
|
||||
// 'Gems' => 'App\Models\Gems',
|
||||
// 'Articles' => 'App\Models\Articles',
|
||||
// ]);
|
||||
}
|
||||
}
|
||||
28
app/Providers/ArticleOrderLikesProvider.php
Executable file
28
app/Providers/ArticleOrderLikesProvider.php
Executable file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Services\CacheService;
|
||||
//use App\Models\Categories;
|
||||
|
||||
class ArticleOrderLikesProvider extends ServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
// 只有渲染 components.order-likes 这个组件时,这段逻辑才会运行
|
||||
View::composer('components.order-likes', function ($oView) {
|
||||
// 从缓存拿数据,拿不到就触发一次更新(高标准兜底)
|
||||
$aHotArticles = Cache::get(CacheService::CACHE_KEYS['article_order_likes']);
|
||||
|
||||
if (is_null($aHotArticles)) {
|
||||
$aHotArticles = CacheService::updateOrderLikes();
|
||||
}
|
||||
|
||||
$oView->with('oOrderLikes', $aHotArticles);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
30
app/Providers/NavServiceProvider.php
Executable file
30
app/Providers/NavServiceProvider.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\Categories;
|
||||
|
||||
class NavServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
// 定义宁要注入数据的视图(可以是具体的 index,也可以是布局 layouts.app)
|
||||
View::composer('components.nav-bar', function ($oView) {
|
||||
|
||||
// 1. 缓存 1 小时 (3600秒),宁写的 1 秒太凑合了,那是“皇帝的缓存”
|
||||
$iCacheTime = 86400;
|
||||
|
||||
// 2. 优雅地从缓存或模型拿数据
|
||||
$aNavs = Cache::remember('global_nav_tree', $iCacheTime, function () {
|
||||
return Categories::getGlobalNavTree();
|
||||
});
|
||||
|
||||
// 3. 把这份艺术品递给视图
|
||||
$oView->with('aNavs', $aNavs);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
277
app/Services/ArticlesService.php
Executable file
277
app/Services/ArticlesService.php
Executable file
@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Categories;
|
||||
use App\Models\Articles;
|
||||
use App\Models\ArticleLikes;
|
||||
use App\Models\Tags;
|
||||
use App\Services\CacheService;
|
||||
|
||||
class ArticlesService
|
||||
{
|
||||
/**
|
||||
* 第一步:文章创建时,生成一个“7天点赞计划”
|
||||
* 规则:第一天占 70%,剩下 6 天分摊 30%,全部为整数
|
||||
*/
|
||||
public function generateSevenDayPlan(int $iTargetTotal): string
|
||||
{
|
||||
if ($iTargetTotal <= 0) return '[]';
|
||||
|
||||
$aPlan = array_fill(0, 7, 0);
|
||||
|
||||
// 1. 第一天分配 70% (向上取整,保证第一天绝对有动作)
|
||||
$iDay1Likes = (int) ceil($iTargetTotal * 0.7);
|
||||
$aPlan[0] = $iDay1Likes;
|
||||
|
||||
// 2. 剩下的 30% 分配给后面 6 天
|
||||
$iRemaining = $iTargetTotal - $iDay1Likes;
|
||||
if ($iRemaining > 0) {
|
||||
// 随机把剩下的点赞塞进后 6 天的坑位里
|
||||
for ($i = 0; $i < $iRemaining; $i++) {
|
||||
$iRandomDay = mt_rand(1, 6);
|
||||
$aPlan[$iRandomDay]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回匈牙利命名法下的 json 字符串
|
||||
return json_encode($aPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二步:执行点赞任务 (建议每小时跑一次)
|
||||
* 只负责把今天该加的赞加进去
|
||||
*/
|
||||
public function executeLikeTask(Article $oArticle): void
|
||||
{
|
||||
if (empty($oArticle->sLikePlan)) return;
|
||||
|
||||
$aPlan = json_decode($oArticle->sLikePlan, true);
|
||||
$iDaysPassed = $oArticle->created_at->diffInDays(now());
|
||||
|
||||
// 如果已经超过7天,或者计划里没这一天,直接撤退
|
||||
if ($iDaysPassed > 6 || !isset($aPlan[$iDaysPassed])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iShouldHaveAdded = $aPlan[$iDaysPassed];
|
||||
|
||||
// 重点:为了不让点赞在凌晨一瞬间加上去,我们可以配合小时数
|
||||
// 比如现在是 14 点,我们应该加到今天总量的 14/24
|
||||
$iHour = (int) date('H');
|
||||
$iCurrentTarget = (int) floor($iShouldHaveAdded * ($iHour / 24));
|
||||
|
||||
// 这里需要一个逻辑记录今天已经加了多少,或者直接根据小时计算增量
|
||||
// 简单处理:我们每天分 24 次把 $iShouldHaveAdded 加完
|
||||
// 为保简洁,这里展示核心逻辑更新:
|
||||
if ($iShouldHaveAdded > 0) {
|
||||
// 算出每小时平均加几个,剩下的随机掉落
|
||||
$iAddToday = $this->calculateHourlyIncrement($iShouldHaveAdded, $iHour);
|
||||
// 这里更新 i7like 字段
|
||||
// 注意:i7like 应该是这篇文章总共通过这个逻辑加了多少赞
|
||||
// ... 具体的数据库 Update 逻辑
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 365天自动点赞:高标准随机触发
|
||||
*/
|
||||
public function applyYearlyGrowth(Article $oArticle): void
|
||||
{
|
||||
$iYearlyTarget = 10; // 哪怕一年只要 10 个
|
||||
|
||||
// 计算今天中奖的概率:10 / 365
|
||||
// 我们放大一万倍来摇号
|
||||
$iProbability = (int)(($iYearlyTarget / 365) * 10000);
|
||||
$iMagicNumber = mt_rand(1, 10000);
|
||||
|
||||
if ($iMagicNumber <= $iProbability) {
|
||||
// 中奖了,加 1 个赞
|
||||
$oArticle->increment('i365like');
|
||||
}
|
||||
}
|
||||
|
||||
public function like(int $iArticleId, string $sIp, string $sFingerprint): array
|
||||
{
|
||||
// 1. 检查是否已经点过
|
||||
$bExists = ArticleLikes::where('iArticleId', $iArticleId)
|
||||
->where(function ($q) use ($sIp, $sFingerprint) {
|
||||
$q->where('sIp', $sIp)
|
||||
->orWhere('sFingerprint', $sFingerprint);
|
||||
})->exists();
|
||||
|
||||
if ($bExists) {
|
||||
return ['status' => false, 'code' => 200, 'msg' => '感谢支持,点过赞了。'];
|
||||
}
|
||||
|
||||
// 2. 事务扣杀
|
||||
try {
|
||||
// 定义一个变量来接收最新的点赞数
|
||||
$iNewLikeCount = 0;
|
||||
|
||||
\DB::transaction(function () use ($iArticleId, $sIp, $sFingerprint, &$iNewLikeCount) {
|
||||
ArticleLikes::create([
|
||||
'iArticleId' => $iArticleId,
|
||||
'sIp' => $sIp,
|
||||
'sFingerprint' => $sFingerprint
|
||||
]);
|
||||
|
||||
// 执行增加并直接获取最新的模型实例
|
||||
$oArticle = Articles::where('id', $iArticleId);
|
||||
$oArticle->increment('iLikeCount');
|
||||
|
||||
// 拿到自增后的最新数字
|
||||
$iNewLikeCount = $oArticle->value('iLikeCount');
|
||||
});
|
||||
|
||||
CacheService::updateOrderLikes();
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'msg' => '感谢支持!',
|
||||
'iLikeCount' => $iNewLikeCount // 药水哥绝杀:把热腾腾的数字发回去
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
\Log::error("点赞失败: " . $e->getMessage());
|
||||
return ['status' => false, 'code' => 500, 'msg' => '系统打了个盹,稍后再试。'];
|
||||
}
|
||||
}
|
||||
|
||||
public function search(string $sKeywords, string $sResultType)
|
||||
{
|
||||
if (empty($sKeywords)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sKeywords = strip_tags(trim($sKeywords));
|
||||
|
||||
$oArticles = Articles::search($sKeywords)
|
||||
->options([
|
||||
'attributesToHighlight' => ['sTitle', 'sContent', 'sCategoryName'],
|
||||
// 1. 把 attributesToSnippet 改成 attributesToCrop
|
||||
'attributesToCrop' => ['sContent'],
|
||||
// 2. 设置裁剪后的长度(单位是字符,按需调整)
|
||||
'cropLength' => 100,
|
||||
'highlightPreTag' => '<mark class="highlight">',
|
||||
'highlightPostTag' => '</mark>',
|
||||
])
|
||||
->orderByDesc('iIsTop')
|
||||
->paginate(config('app.page_limit'));
|
||||
|
||||
$oArticles->getCollection()->transform(function ($oArticle) {
|
||||
|
||||
$aMeta = $oArticle->scoutMetadata();
|
||||
$aFormatted = $aMeta['_formatted'] ?? [];
|
||||
|
||||
if (!empty($aFormatted)) {
|
||||
$oArticle->sTitle = $aFormatted['sTitle'] ?? $oArticle->sTitle;
|
||||
$oArticle->sContent = $aFormatted['sContent'] ?? $oArticle->sContent;
|
||||
|
||||
if (isset($aFormatted['sCategoryName'])) {
|
||||
$oArticle->sCategoryName = $aFormatted['sCategoryName'];
|
||||
$oArticle->setRelation('oCategory', (object)[
|
||||
'sName' => $aFormatted['sCategoryName'],
|
||||
'sSlug' => $oArticle->sCategorySlug ?? ''
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $oArticle;
|
||||
});
|
||||
|
||||
if ($sResultType === "ajax") {
|
||||
$sHtml = '';
|
||||
foreach ($oArticles as $oArticle) {
|
||||
$sHtml .= view('components.article-item', ['oArticle' => $oArticle])->render();
|
||||
}
|
||||
$xResult = $sHtml;
|
||||
} else {
|
||||
$xResult = [
|
||||
'oArticles' => $oArticles,
|
||||
];
|
||||
}
|
||||
|
||||
return $xResult;
|
||||
}
|
||||
|
||||
public function getTagArticles(string $sSlug, string $sResultType)
|
||||
{
|
||||
$oTags = Tags::where('sSlug', $sSlug)->firstOrFail();
|
||||
|
||||
$oArticles = $oTags->oArticles()
|
||||
->where('iStatus', 1) // 只拿已发布的
|
||||
->with(['oCategory']) // 预加载关联,拒绝 N+1
|
||||
->orderByDesc('iIsTop') // 置顶优先
|
||||
->orderByDesc('created_at') // 最新优先
|
||||
->paginate(config('app.page_limit')); // 必须分页,否则数据多了会爆内存
|
||||
|
||||
if ($sResultType === "ajax") {
|
||||
$sHtml = '';
|
||||
foreach ($oArticles as $oArticle) {
|
||||
$sHtml .= view('components.article-item', ['oArticle' => $oArticle])->render();
|
||||
}
|
||||
$xResult = $sHtml;
|
||||
} else {
|
||||
$xResult = [
|
||||
'oArticles' => $oArticles,
|
||||
];
|
||||
}
|
||||
|
||||
return $xResult;
|
||||
}
|
||||
|
||||
public function getCategoryArticles(string $sSlug, string $sResultType)
|
||||
{
|
||||
$oCategory = Categories::where('sSlug', $sSlug)->firstOrFail();
|
||||
|
||||
if ($oCategory->iShowChildren) {
|
||||
$aCategoryIds = Categories::where('iParentId', $oCategory->id)
|
||||
->pluck('id')
|
||||
->push($oCategory->id)
|
||||
->toArray();
|
||||
} else {
|
||||
$aCategoryIds = [$oCategory->id];
|
||||
}
|
||||
|
||||
$oArticles = Articles::whereIn('iCategoryId', $aCategoryIds)
|
||||
->with(['oUser', 'oCategory'])
|
||||
->orderBy('iIsTop', 'desc')
|
||||
->orderBy('created_at', 'desc')
|
||||
->simplePaginate(config('app.page_limit'));
|
||||
|
||||
if ($sResultType === "ajax") {
|
||||
$sHtml = '';
|
||||
foreach ($oArticles as $oArticle) {
|
||||
$sHtml .= view('components.article-item', ['oArticle' => $oArticle])->render();
|
||||
}
|
||||
$aResult = $sHtml;
|
||||
} else {
|
||||
$aResult = [
|
||||
'oCategory' => $oCategory,
|
||||
'oArticles' => $oArticles,
|
||||
];
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function getArticle(string $sArticleSlug, string $sIp, string $sFingerprint)
|
||||
{
|
||||
$oArticle = Articles::where('sSlug', $sArticleSlug)
|
||||
->where('iStatus', 1)
|
||||
->with(['oCategory.oParent', 'oTags'])
|
||||
->firstOrFail();
|
||||
|
||||
$oArticle->bIsLiked = ArticleLikes::where('iArticleId', $oArticle->id)
|
||||
->where(function($q) use ($sIp, $sFingerprint) {
|
||||
$q->where('sIp', $sIp)
|
||||
->orWhere('sFingerprint', $sFingerprint);
|
||||
})->exists();
|
||||
|
||||
// return $oArticle;
|
||||
return [
|
||||
'oArticle' => $oArticle
|
||||
];
|
||||
}
|
||||
}
|
||||
68
app/Services/CacheService.php
Executable file
68
app/Services/CacheService.php
Executable file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
//use App\Models\Categories;
|
||||
use App\Models\Articles;
|
||||
use App\Models\ArticleLikes;
|
||||
//use App\Models\Tags;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class CacheService
|
||||
{
|
||||
public const CACHE_KEYS = [
|
||||
'article_order_likes' => 'article_order_likes',
|
||||
];
|
||||
|
||||
// public static function updateOrderLikes(): array
|
||||
// {
|
||||
// $oStartDate = now()->subDays(30);
|
||||
//
|
||||
// $aHotArticles = ArticleLikes::select('iArticleId', \DB::raw('count(*) as iTotalLikes'))
|
||||
// ->where('created_at', '>=', $oStartDate)
|
||||
// ->groupBy('iArticleId')
|
||||
// ->orderBy('iTotalLikes', 'desc')
|
||||
// ->with(['oArticle' => function($oQuery) {
|
||||
// $oQuery->where('iStatus', 1);
|
||||
// }])
|
||||
// ->limit(10)
|
||||
// ->get()
|
||||
// ->filter(fn($oItem) => $oItem->oArticle !== null)
|
||||
// ->toArray(); // 存入缓存建议转成数组,性能更稳
|
||||
//
|
||||
// // 存入缓存,永久保存(直到下一次更新)
|
||||
// Cache::forever(self::CACHE_KEYS['article_order_likes'], $aHotArticles);
|
||||
//
|
||||
// return $aHotArticles;
|
||||
// }
|
||||
|
||||
public static function updateOrderLikes(): array
|
||||
{
|
||||
// 1. 获取最近点赞的文章ID流
|
||||
// 使用 max(id) 或 max(created_at) 来确保每个文章只出现一次,且取的是它最后一次被点赞的时间
|
||||
$aHotArticles = ArticleLikes::select('iArticleId', \DB::raw('MAX(created_at) as sLatestLikeTime'))
|
||||
->whereHas('oArticle', function($oQuery) {
|
||||
$oQuery->where('iStatus', 1); // 只看发布状态的文章
|
||||
})
|
||||
->groupBy('iArticleId')
|
||||
->orderBy('sLatestLikeTime', 'desc') // 按最后一次点赞时间倒序
|
||||
->limit(10)
|
||||
->with('oArticle') // 预加载文章详情
|
||||
->get()
|
||||
->map(function($oItem) {
|
||||
// 格式化输出,把文章对象和点赞时间揉在一起
|
||||
return [
|
||||
'iArticleId' => $oItem->iArticleId,
|
||||
'sLatestLikeTime' => $oItem->sLatestLikeTime,
|
||||
'oArticle' => $oItem->oArticle
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
// 2. 存入缓存
|
||||
Cache::forever(self::CACHE_KEYS['article_order_likes'], $aHotArticles);
|
||||
|
||||
return $aHotArticles;
|
||||
}
|
||||
|
||||
}
|
||||
70
app/Services/TagService.php
Executable file
70
app/Services/TagService.php
Executable file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Articles;
|
||||
use App\Models\Tags;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TagService
|
||||
{
|
||||
/**
|
||||
* 给文章同步中文标签
|
||||
* @param Article $oArticle 文章对象
|
||||
* @param array $asTagNames 标签名数组 ['药水哥', '直播', 'Laravel']
|
||||
*/
|
||||
public function syncArticleTagsByName(Articles $oArticle, array $asTagNames): void
|
||||
{
|
||||
if (empty($asTagNames)) {
|
||||
$oArticle->oTags()->detach();
|
||||
return;
|
||||
}
|
||||
|
||||
$oPinyin = new Pinyin();
|
||||
|
||||
// 1. 处理标签:不存在则创建
|
||||
$aTagIds = collect($asTagNames)
|
||||
->filter()
|
||||
->unique()
|
||||
->map(function ($sName) use ($oPinyin) {
|
||||
// 修正:sName作为查找条件
|
||||
$oTag = Tags::firstOrCreate(
|
||||
['sName' => trim($sName)],
|
||||
[
|
||||
// 生成拼音 Slug,例如:药水哥 -> yao-shui-ge
|
||||
'sSlug' => Str::slug($oPinyin->permalink($sName)) . '-' . Str::random(5)
|
||||
]
|
||||
);
|
||||
return $oTag->id;
|
||||
})
|
||||
->all();
|
||||
|
||||
// 2. 数据库事务处理
|
||||
DB::transaction(function () use ($oArticle, $aTagIds) {
|
||||
// 获取变更前的标签ID,用于后续精准更新计数
|
||||
$aOldTagIds = $oArticle->oTags()->pluck('id')->toArray();
|
||||
|
||||
// 同步中间表
|
||||
$oArticle->oTags()->sync($aTagIds);
|
||||
|
||||
// 3. 高标准:精准更新受影响标签的 iArticleCount
|
||||
$aAffectedTagIds = array_unique(array_merge($aOldTagIds, $aTagIds));
|
||||
if (!empty($aAffectedTagIds)) {
|
||||
$this->refreshTagCounts($aAffectedTagIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 精准刷新指定标签的文章计数
|
||||
*/
|
||||
protected function refreshTagCounts(array $aTagIds): void
|
||||
{
|
||||
foreach ($aTagIds as $iTagId) {
|
||||
$iCount = DB::table('article_tag')->where('iTagId', $iTagId)->count();
|
||||
Tags::where('id', $iTagId)->update(['iArticleCount' => $iCount]);
|
||||
}
|
||||
}
|
||||
}
|
||||
1052
app/Services/Tg/Hook/Dog.php
Executable file
1052
app/Services/Tg/Hook/Dog.php
Executable file
File diff suppressed because it is too large
Load Diff
57
app/Services/Tg/Hook/Mod/Base.php
Executable file
57
app/Services/Tg/Hook/Mod/Base.php
Executable file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
//use App\Services\TomTool\Http;
|
||||
//use App\Models\Article as ModelArticle;
|
||||
use App\Services\TomTool\Http;
|
||||
use App\Services\Tg\Http as TGHttp;
|
||||
|
||||
class Base {
|
||||
|
||||
public function __construct() {
|
||||
$this->oHttp = new Http();
|
||||
$this->oTGHttp = new TGHttp();
|
||||
// $this->oTGHttp->sApiToken = env("telegram_token");
|
||||
// $this->oTGHttp->sApiChatId = env("telegram_chatid");
|
||||
}
|
||||
|
||||
public function toJson($aData)
|
||||
{
|
||||
return json_encode($aData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
public function eachSendTg($aData)
|
||||
{
|
||||
foreach ($aData as $row) {
|
||||
$this->oTGHttp->sendToTG(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
$this->oHttp::response(200, 'ok');
|
||||
}
|
||||
|
||||
// protected function cmdGood($sCmd) {
|
||||
//
|
||||
// $sCmdNew = "";
|
||||
// switch ($sCmd) {
|
||||
// case "user":
|
||||
// $sCmdNew = "user_name";
|
||||
// break;
|
||||
// case "project":
|
||||
// $sCmdNew = "project_name";
|
||||
// break;
|
||||
// case "code":
|
||||
// $sCmdNew = "sale_code";
|
||||
// break;
|
||||
// case "condition":
|
||||
// $sCmdNew = "cron_condition";
|
||||
// break;
|
||||
// default:
|
||||
// $sCmdNew = $sCmd;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// return $sCmdNew;
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
48
app/Services/Tg/Hook/Mod/Console.php
Executable file
48
app/Services/Tg/Hook/Mod/Console.php
Executable file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
use App\Models\Option as ModelOption;
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class Console extends Base {
|
||||
|
||||
private $oDog;
|
||||
|
||||
public function __construct($aUpdate = [])
|
||||
{
|
||||
$this->oDog = new Dog(new ModelOption());
|
||||
// $this->oDog->_setPrimaryKey("k"); // 可选,默认id
|
||||
$this->oDog->_setDogName("console"); // 必须
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
public function call($aParam)
|
||||
{
|
||||
$sName = $aParam["aArg"][1] ?? '';
|
||||
|
||||
if ($sName == "?") {
|
||||
return "call [sName] 案例:stat:tags-count";
|
||||
}
|
||||
|
||||
if (!$sName) {
|
||||
return "需要name";
|
||||
}
|
||||
|
||||
$iExitCode = Artisan::call($sName);
|
||||
$sOutput = Artisan::output();
|
||||
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
public function t()
|
||||
{
|
||||
echo 11;
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
49
app/Services/Tg/Hook/Mod/Option.php
Executable file
49
app/Services/Tg/Hook/Mod/Option.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
use App\Models\Option as ModelOption;
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
|
||||
class Option extends Base {
|
||||
|
||||
private $oDog;
|
||||
|
||||
public function __construct($aUpdate = [])
|
||||
{
|
||||
$this->oDog = new Dog(new ModelOption());
|
||||
$this->oDog->_setPrimaryKey("k"); // 可选,默认id
|
||||
$this->oDog->_setDogName("option"); // 必须
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
// public function Pause($aParam)
|
||||
// {
|
||||
// $v = $aParam['aArg'][1];
|
||||
//
|
||||
// // 获取第一个匹配的选项
|
||||
// $oOption = ModelOption::where("type", "pause")->first();
|
||||
//
|
||||
// // 检查是否找到了选项
|
||||
// if ($oOption) {
|
||||
// $oOption->value = $v;
|
||||
//
|
||||
// // 保存并返回结果
|
||||
// return json_encode($oOption->save());
|
||||
// }
|
||||
//
|
||||
// // 如果没有找到选项,返回一个错误信息
|
||||
// return json_encode(['error' => 'Option not found']);
|
||||
// }
|
||||
//
|
||||
// public function List()
|
||||
// {
|
||||
// $aOptionList = ModelOption::all()->toArray();
|
||||
//
|
||||
// return json_encode($aOptionList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
// }
|
||||
|
||||
}
|
||||
108
app/Services/Tg/Http.php
Executable file
108
app/Services/Tg/Http.php
Executable file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg;
|
||||
use App\Services\TomTool\Http as TomHttp;
|
||||
|
||||
class Http {
|
||||
|
||||
public $sApiToken;
|
||||
public $sApiChatId;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->sApiToken = env('telegram_token');
|
||||
$this->sApiChatId = env('telegram_chatid');
|
||||
}
|
||||
|
||||
public function send($sMsg = "msg空", $aUpdate = [])
|
||||
{
|
||||
|
||||
$bSendTg = true;
|
||||
|
||||
$sShell = $aUpdate["shell"] ?? false;
|
||||
$bIsDove = $aUpdate["is_dove"] ?? false;
|
||||
|
||||
if ($sShell == "cmd") {
|
||||
$bSendTg = false;
|
||||
}
|
||||
|
||||
if ($bIsDove == true) {
|
||||
$bSendTg = false;
|
||||
}
|
||||
|
||||
if (env("is_loc") == true) {
|
||||
$bSendTg = false;
|
||||
}
|
||||
|
||||
if (is_array($sMsg)) {
|
||||
if ($bSendTg) {
|
||||
foreach ($sMsg as $sMsgRow) {
|
||||
$this->sendTg($sMsgRow);
|
||||
}
|
||||
} else {
|
||||
echo json_encode($sMsg, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
} else {
|
||||
if ($bSendTg) {
|
||||
$this->sendTg($sMsg);
|
||||
} else {
|
||||
echo $sMsg;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bSendTg) {
|
||||
TomHttp::response(200);
|
||||
} else {
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function sendTg($sMsg)
|
||||
{
|
||||
$oHttp = new TomHttp();
|
||||
$oHttp->sMethod = "post";
|
||||
$oHttp->sToUrl = "https://api.telegram.org/bot$this->sApiToken/sendMessage";
|
||||
// $oHttp->bIsJson = true;
|
||||
$oHttp->aData = [
|
||||
"chat_id" => $this->sApiChatId,
|
||||
"text" => $sMsg
|
||||
];
|
||||
|
||||
$oHttp->send();
|
||||
}
|
||||
|
||||
public function sendToTG($sMsg, $aUpdate = [])
|
||||
{
|
||||
|
||||
if (isset($aUpdate["shell"]) && $aUpdate["shell"] == "cmd") {
|
||||
|
||||
echo $sMsg;
|
||||
|
||||
} else if (isset($aUpdate["is_dove"]) && $aUpdate["is_dove"] == true) {
|
||||
|
||||
echo $sMsg;
|
||||
|
||||
} else {
|
||||
|
||||
if (env("is_loc") == true) {
|
||||
echo $sMsg;
|
||||
} else {
|
||||
$oHttp = new TomHttp();
|
||||
$oHttp->sMethod = "post";
|
||||
$oHttp->sToUrl = "https://api.telegram.org/bot$this->sApiToken/sendMessage";
|
||||
// $oHttp->bIsJson = true;
|
||||
$oHttp->aData = [
|
||||
"chat_id" => $this->sApiChatId,
|
||||
"text" => $sMsg
|
||||
];
|
||||
|
||||
$oHttp->send();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
425
app/Services/TomTool/Flow.php
Executable file
425
app/Services/TomTool/Flow.php
Executable file
@ -0,0 +1,425 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use App\Services\TomTool\TelegramVdb;
|
||||
|
||||
/**
|
||||
|
||||
默认:
|
||||
name:none
|
||||
isDone:false
|
||||
|
||||
new:
|
||||
$oResult = Flow::make("name");
|
||||
|
||||
快速:
|
||||
Flow::make("name")->fail("kkkkkkk");
|
||||
|
||||
进阶:
|
||||
Flow::make("name")->noDone()->msg("哟哟哟哟哟哟")->sendTg()->fetch();
|
||||
|
||||
业务:
|
||||
step()
|
||||
msg()
|
||||
data()
|
||||
aData()
|
||||
jData()
|
||||
sData()
|
||||
code()
|
||||
put()
|
||||
merge()
|
||||
toDone()
|
||||
noDone()
|
||||
step("start")
|
||||
|
||||
action:
|
||||
reset()
|
||||
done(?$msg)
|
||||
fail(?$msg, ?$aParentResult)
|
||||
sendTg() 伪action
|
||||
fetch() or get()
|
||||
fetchAll() or geAll()
|
||||
|
||||
set:
|
||||
enableTg();
|
||||
parent($aParentResult);
|
||||
|
||||
with:
|
||||
withTraceId();
|
||||
withTimestamp();
|
||||
withStep();
|
||||
|
||||
判断:
|
||||
::isDone($aResult);
|
||||
::isFail($aResult);
|
||||
|
||||
*/
|
||||
|
||||
class Flow
|
||||
{
|
||||
protected array $aStep = [];
|
||||
protected array $aParent = [];
|
||||
protected bool $isToTg = false;
|
||||
protected bool $isToTgOne = false;
|
||||
protected array $aResult = [
|
||||
'isDone' => false,
|
||||
];
|
||||
protected string $sName;
|
||||
private bool $isChain = false;
|
||||
|
||||
public function __construct(string $name = 'none')
|
||||
{
|
||||
$this->sName = $name;
|
||||
$this->aStep[] = "{$this->sName}::start";
|
||||
}
|
||||
|
||||
// new
|
||||
public static function make(string $name = 'none'): self
|
||||
{
|
||||
return new self($name);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function start($sName = 'none')
|
||||
{
|
||||
$oInstance = new self($sName);
|
||||
$oInstance->isChain = true;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
// option
|
||||
public function enableTg(bool $flag = true): self
|
||||
{
|
||||
$this->isToTg = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// option
|
||||
public function enableTgOne(bool $flag = true): self
|
||||
{
|
||||
$this->enableTg(true);
|
||||
$this->isToTgOne = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// option
|
||||
public function parent(array $aParentResult): self
|
||||
{
|
||||
$this->aParent = $aParentResult;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function step(string $step): self
|
||||
{
|
||||
$this->aStep[] = "{$this->sName}::{$step}";
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function msg($msg = "msg未设置"): self
|
||||
{
|
||||
if (is_array($msg)) {
|
||||
$msg = json_encode($msg);
|
||||
}
|
||||
$this->put('sMsg', $msg);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function aData(array $data): self
|
||||
{
|
||||
$this->put('aData', $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function aDataPut($k, $v)
|
||||
{
|
||||
$this->aResult["aData"][$k] = $v;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function sData(string $data): self
|
||||
{
|
||||
$this->put('sData', $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function code(string $code): self
|
||||
{
|
||||
$this->put('sCode', $code);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function put(string $key, mixed $value, $sDataKey = ''): self
|
||||
{
|
||||
if ($sDataKey) {
|
||||
$this->aResult[$sDataKey][$key] = $value;
|
||||
} else {
|
||||
$this->aResult[$key] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function merge(array $data): self
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
$this->put($k, $v);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function setDone(bool $b = true): self
|
||||
{
|
||||
$this->aResult['isDone'] = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function setFail(string $step = ''): self
|
||||
{
|
||||
if ($step) {
|
||||
$this->step($step);
|
||||
}
|
||||
$this->aResult['isDone'] = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// with
|
||||
public function withTraceId(string $id = null): self
|
||||
{
|
||||
$this->aResult['sTraceId'] = $id ?? uniqid('trace_', true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// with
|
||||
public function withTimestamp(): self
|
||||
{
|
||||
$this->aResult['sTimestamp'] = (new \DateTime())->format(\DateTime::ATOM);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// with
|
||||
public function withStep(bool $flag = true): self
|
||||
{
|
||||
if ($flag) {
|
||||
$this->withParentStep();
|
||||
$this->aResult['aStep'] = $this->aStep;
|
||||
} else {
|
||||
unset($this->aResult['aStep']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// action
|
||||
public function reset(): self
|
||||
{
|
||||
$this->aStep = [$this->sName . '::start'];
|
||||
$this->aParent = [];
|
||||
$this->isToTg = false;
|
||||
$this->isToTgOne = false;
|
||||
$this->aResult = ['isDone' => false];
|
||||
return $this;
|
||||
}
|
||||
|
||||
// action
|
||||
public function done(mixed $msg = null)
|
||||
{
|
||||
$this->setDone();
|
||||
if ($msg) {
|
||||
$this->msg($msg);
|
||||
}
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
// action
|
||||
public function fail(mixed $msg = null, array $aParentResult = [])
|
||||
{
|
||||
$this->setFail('fail');
|
||||
if ($aParentResult) {
|
||||
$this->aParent = $aParentResult;
|
||||
}
|
||||
if ($msg) {
|
||||
$this->msg($msg);
|
||||
}
|
||||
return $this->getFull();
|
||||
}
|
||||
|
||||
// action
|
||||
// public function sendTg() // 伪装成非set
|
||||
// {
|
||||
// $this->enableTgOne();
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
// action
|
||||
public function sendTg($sBotCode = 'base', $sChatCode = 'base')
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getDataA()
|
||||
{
|
||||
return $this->aResult["aData"] ?? [];
|
||||
}
|
||||
|
||||
public function getDataS()
|
||||
{
|
||||
return (string) $this->aResult["sData"] ?? '';
|
||||
}
|
||||
|
||||
public function getDataI()
|
||||
{
|
||||
return (int) $this->aResult["iData"] ?? 0;
|
||||
}
|
||||
|
||||
public function getDataB()
|
||||
{
|
||||
return (bool) $this->aResult["bData"] ?? false;
|
||||
}
|
||||
|
||||
public function getDataX()
|
||||
{
|
||||
return $this->aResult["xData"] ?? '';
|
||||
}
|
||||
|
||||
public function setDataA(array $aData): self
|
||||
{
|
||||
$this->aData($aData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataB(bool $bData): self
|
||||
{
|
||||
$this->aResult["bData"] = $bData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataS(string $sData): self
|
||||
{
|
||||
$this->aResult["sData"] = $sData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataI(int $iData): self
|
||||
{
|
||||
$this->aResult["iData"] = $iData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataX($xData): self
|
||||
{
|
||||
$this->aResult["xData"] = $xData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResult(): array
|
||||
{
|
||||
return $this->aResult;
|
||||
}
|
||||
|
||||
// action
|
||||
public function get()
|
||||
{
|
||||
if ($this->isToTg) {
|
||||
$this->_sendTg();
|
||||
}
|
||||
|
||||
if ($this->isToTgOne) {
|
||||
$this->isToTg = false;
|
||||
$this->isToTgOne = false;
|
||||
}
|
||||
|
||||
if ($this->isChain) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->aResult;
|
||||
}
|
||||
|
||||
// action
|
||||
public function getFull()
|
||||
{
|
||||
$this->withStep();
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
// action
|
||||
public function toArray()
|
||||
{
|
||||
return $this->aResult;
|
||||
}
|
||||
|
||||
// action
|
||||
public function toJson($bFormat = true)
|
||||
{
|
||||
if ($bFormat === true) {
|
||||
return json_encode($this->aResult, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
return json_encode($this->aResult);
|
||||
}
|
||||
}
|
||||
|
||||
// helper
|
||||
public function isDone()
|
||||
{
|
||||
return $this->aResult["isDone"] ?? false;
|
||||
}
|
||||
|
||||
// helper
|
||||
public function isFail()
|
||||
{
|
||||
return !$this->isDone();
|
||||
}
|
||||
|
||||
// helper
|
||||
public static function isDoneResult(array $aResult): bool
|
||||
{
|
||||
return $aResult['isDone'] ?? false;
|
||||
}
|
||||
|
||||
// helper
|
||||
public static function isFailResult(array $aResult): bool
|
||||
{
|
||||
return !self::isDoneResult($aResult);
|
||||
}
|
||||
|
||||
// shell
|
||||
public function fetch()
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
// shell
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->getFull();
|
||||
}
|
||||
|
||||
private function _sendTg()
|
||||
{
|
||||
TelegramVdb::make()->dataArr($this->aResult)->save();
|
||||
}
|
||||
|
||||
protected function addStepParent(): void
|
||||
{
|
||||
$this->aStep[] = $this->aParent;
|
||||
}
|
||||
|
||||
protected function withParentStep(): void
|
||||
{
|
||||
if ($this->aParent) {
|
||||
$this->addStepParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
app/Services/TomTool/Http.php
Executable file
93
app/Services/TomTool/Http.php
Executable file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
class Http
|
||||
{
|
||||
public $sMethod = 'get';
|
||||
public $aData = [];
|
||||
public $sToUrl; // 必须
|
||||
public $bIsJson = false;
|
||||
|
||||
public function send($sUrl = false): string
|
||||
{
|
||||
// 两种传递方式
|
||||
if ($sUrl) {
|
||||
$this->sToUrl = $sUrl;
|
||||
}
|
||||
|
||||
// 初始化 cURL 会话
|
||||
$ch = curl_init();
|
||||
|
||||
// 设置请求 URL
|
||||
curl_setopt($ch, CURLOPT_URL, $this->sToUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应而不是输出
|
||||
|
||||
// 根据请求方法设置 cURL 选项
|
||||
switch (strtolower($this->sMethod)) {
|
||||
case 'post':
|
||||
curl_setopt($ch, CURLOPT_POST, true); // 设置为 POST 请求
|
||||
if ($this->bIsJson) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->aData)); // 设置 POST 数据为 JSON
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json', // 设置内容类型为 JSON
|
||||
'Content-Length: ' . strlen(json_encode($this->aData)) // 设置内容长度
|
||||
]);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->aData)); // 设置 POST 数据为普通表单格式
|
||||
}
|
||||
break;
|
||||
|
||||
case 'put':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); // 设置为 PUT 请求
|
||||
if ($this->bIsJson) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->aData)); // 设置 PUT 数据为 JSON
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json', // 设置内容类型为 JSON
|
||||
'Content-Length: ' . strlen(json_encode($this->aData)) // 设置内容长度
|
||||
]);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->aData)); // 设置 PUT 数据为普通表单格式
|
||||
}
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
// 默认使用 GET 请求
|
||||
curl_setopt($ch, CURLOPT_HTTPGET, true); // 设置为 GET 请求
|
||||
curl_setopt($ch, CURLOPT_URL, $this->sToUrl . '?' . http_build_query($this->aData)); // 设置请求 URL
|
||||
if ($this->bIsJson) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: application/json' // 设置接受的内容类型为 JSON
|
||||
]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 禁用 SSL 验证
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证对等证书
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证主机名
|
||||
|
||||
// 执行 cURL 请求
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// 检查是否有错误
|
||||
if (curl_errno($ch)) {
|
||||
return 'Error: ' . curl_error($ch);
|
||||
}
|
||||
|
||||
// 关闭 cURL 会话
|
||||
curl_close($ch);
|
||||
|
||||
// 返回响应
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function response($iCode, $sMsg = '')
|
||||
{
|
||||
http_response_code($iCode);
|
||||
echo $sMsg;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
203
app/Services/TomTool/HttpV2.php
Executable file
203
app/Services/TomTool/HttpV2.php
Executable file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
// 版本:25-12-12
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use App\Services\TomTool\Flow;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
|
||||
默认:
|
||||
method:post
|
||||
|
||||
new:
|
||||
HttpV2::make($url)
|
||||
|
||||
基本:
|
||||
HttpV2::make($url)->send()
|
||||
|
||||
进阶:
|
||||
HttpV2::make($url, "get")->setDataA($aData)->enableJsonAs()->enableAsync()->optMethod("get")->send();
|
||||
|
||||
action:
|
||||
send()
|
||||
|
||||
data:
|
||||
xData()
|
||||
sData()
|
||||
jData()
|
||||
sData()
|
||||
|
||||
set:
|
||||
optMethod()
|
||||
enableJsonAs()
|
||||
enableJsonSend()
|
||||
enableJsonReturn()
|
||||
|
||||
@note:
|
||||
默认关闭 SSL 验证(适用于开发环境),生产环境建议开启
|
||||
异步模式下不会返回响应内容,仅快速触发请求
|
||||
|
||||
*/
|
||||
|
||||
|
||||
class HttpV2
|
||||
{
|
||||
protected string $method;
|
||||
protected $data = [];
|
||||
protected string $url;
|
||||
protected bool $sendJson = false;
|
||||
protected bool $returnJson = false;
|
||||
protected bool $async = false;
|
||||
// 匈牙利命名法,仅新增一个 token 变量
|
||||
protected string $sToken = '';
|
||||
|
||||
public static function make(string $url, string $method = 'post'): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->url = $url;
|
||||
$instance->optMethod($method);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function optMethod(string $method): self
|
||||
{
|
||||
$this->method = strtolower($method);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:支持携带 Token
|
||||
* 如果不传值,默认从环境变量 token_base 获取
|
||||
*/
|
||||
public function withToken(?string $sToken = null): self
|
||||
{
|
||||
$this->sToken = $sToken ?? (string)env('token_base', false);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataX($xData)
|
||||
{
|
||||
$this->setData($xData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataA($aData)
|
||||
{
|
||||
$this->setData($aData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataS($sData)
|
||||
{
|
||||
$this->setData($sData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setData($xData)
|
||||
{
|
||||
$this->data = $xData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableJsonSend(bool $flag = true): self
|
||||
{
|
||||
$this->sendJson = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableJsonReturn(bool $flag = true): self
|
||||
{
|
||||
$this->returnJson = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableJsonAs(): self
|
||||
{
|
||||
$this->sendJson = true;
|
||||
$this->returnJson = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableAsync(bool $flag = true): self
|
||||
{
|
||||
$this->async = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send(): string
|
||||
{
|
||||
$oFlow = Flow::make("HttpV2::send");
|
||||
|
||||
$ch = curl_init();
|
||||
$headers = [];
|
||||
|
||||
// --- 核心嵌入:如果 sToken 有值且不是 false,塞进 Header ---
|
||||
if ($this->sToken && $this->sToken !== 'false') {
|
||||
$headers[] = 'Authorization: Bearer ' . $this->sToken;
|
||||
}
|
||||
|
||||
// 构建 URL 和请求体 (完全保留原样)
|
||||
if ($this->method === 'get') {
|
||||
$url = $this->url . '?' . http_build_query($this->data);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
$payload = $this->sendJson ? json_encode($this->data) : http_build_query($this->data);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
|
||||
if ($this->method === 'post') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method));
|
||||
}
|
||||
|
||||
if ($this->sendJson) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers[] = 'Content-Length: ' . strlen($payload);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->returnJson) {
|
||||
$headers[] = 'Accept: application/json';
|
||||
}
|
||||
|
||||
if (!empty($headers)) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
|
||||
// SSL 设置 (完全保留原样)
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
// 异步发送 (完全保留原样)
|
||||
if ($this->async) {
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 200);
|
||||
curl_setopt($ch, CURLOPT_NOSIGNAL, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return 'sent';
|
||||
}
|
||||
|
||||
// 阻塞执行 (完全保留原样)
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception("cURL Error: {$error}");
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
26
app/Services/TomTool/Map.php
Executable file
26
app/Services/TomTool/Map.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
class Map
|
||||
{
|
||||
public static function fnClientToSfx($sClient)
|
||||
{
|
||||
$sfx = "";
|
||||
|
||||
switch ($sClient) {
|
||||
case "clash":
|
||||
$sfx = "yaml";
|
||||
break;
|
||||
case "v2ray":
|
||||
$sfx = "txt";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $sfx;
|
||||
}
|
||||
}
|
||||
177
app/Services/TomTool/Telegram/Master.php
Executable file
177
app/Services/TomTool/Telegram/Master.php
Executable file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool\Telegram;
|
||||
|
||||
use App\Services\HttpQueue as ServiceHttpQueue;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Services\TomTool\Telegram\Sdk as ServiceTelegramSdk;
|
||||
use App\Models\TelegramKey as ModelTelegramKey;
|
||||
|
||||
/**
|
||||
|
||||
注意是master服务器端专用的,这里会对code找map,slave不会。
|
||||
|
||||
new:
|
||||
start(?$sBodeCode, ?$sChatCode)
|
||||
|
||||
set:
|
||||
setBotCode()
|
||||
setChatCode()
|
||||
setBotToken()
|
||||
setChatId()
|
||||
setMsgS()
|
||||
|
||||
action:
|
||||
send()
|
||||
|
||||
*/
|
||||
|
||||
final class Master
|
||||
{
|
||||
public string $sBotCode = "base";
|
||||
public string $sChatCode = "base";
|
||||
public string $sBotToken = '';
|
||||
public int $iChatId = 0;
|
||||
|
||||
public string $sMsg = '';
|
||||
public string $sPhotoUrl = '';
|
||||
public int $iPhotoWidth = 0;
|
||||
public int $iPhotoHeight = 0;
|
||||
|
||||
public string $sFileUrl = '';
|
||||
public string $sFileName = '';
|
||||
|
||||
public string $sType = '';
|
||||
|
||||
public bool $enableQueue = false; // 应该用不到了
|
||||
|
||||
public bool $bKeyBegin = false;
|
||||
|
||||
public static function start($sBotCode = 'base', $sChatCode = 'base'): self
|
||||
{
|
||||
$oInstance = new self();
|
||||
$oInstance->sBotCode = $sBotCode;
|
||||
$oInstance->sChatCode = $sChatCode;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function setBotCode(string $sBotCode): self
|
||||
{
|
||||
$this->sBotCode = $sBotCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatCode(string $sChatCode): self
|
||||
{
|
||||
$this->sChatCode = $sChatCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBotToken(string $sBotToken): self
|
||||
{
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatId(int $iChatId): self
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMsgS(string $sMsg): self
|
||||
{
|
||||
$this->sMsg = $sMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhotoUrl(string $sPhotoUrl, int $iPhotoWidth = 0, int $iPhotoHeight = 0): self
|
||||
{
|
||||
$this->sPhotoUrl = $sPhotoUrl;
|
||||
$this->iPhotoWidth = $iPhotoWidth;
|
||||
$this->iPhotoHeight = $iPhotoHeight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(string $sType): self
|
||||
{
|
||||
$this->sType = $sType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFileUrl(string $sFileUrl = '', string $sFileName = ''): self
|
||||
{
|
||||
$this->sFileUrl = $sFileUrl;
|
||||
$this->sFileName = $sFileName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send($sMsg = ''): Flow
|
||||
{
|
||||
if ($sMsg) {
|
||||
$this->sMsg = $sMsg;
|
||||
}
|
||||
|
||||
if (!$this->sMsg) {
|
||||
// throw new \Exception("未设置msg");
|
||||
}
|
||||
|
||||
$this->keyBegin();
|
||||
|
||||
if ($this->enableQueue) {
|
||||
$aData = $this->buildDataTom();
|
||||
$oFlow = ServiceHttpQueue::start("telegram_local")->arg($aData)->save();
|
||||
} else {
|
||||
$oFlow = ServiceTelegramSdk::start()
|
||||
->setBotToken($this->sBotToken)
|
||||
->setChatId($this->iChatId)
|
||||
->setMsgS($this->sMsg)
|
||||
->setPhotoUrl($this->sPhotoUrl, $this->iPhotoWidth, $this->iPhotoHeight)
|
||||
->setFileUrl($this->sFileUrl, $this->sFileName)
|
||||
->setType($this->sType)
|
||||
->send();
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
private function keyBegin()
|
||||
{
|
||||
if (!$this->bKeyBegin) {
|
||||
if (!$this->sBotToken) {
|
||||
$this->sBotToken = ModelTelegramKey::botTokenByCode($this->sBotCode);
|
||||
}
|
||||
|
||||
if (!$this->iChatId) {
|
||||
$this->iChatId = ModelTelegramKey::chatIdByCode($this->sChatCode);
|
||||
}
|
||||
|
||||
if (!$this->sBotToken || !$this->iChatId) {
|
||||
throw new \RuntimeException("botToken或chatId没找到,botCode={$this->sBotCode},chatCode={$this->sChatCode}");
|
||||
}
|
||||
|
||||
$this->bKeyBegin = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildDataTom(): array
|
||||
{
|
||||
return [
|
||||
"sBotCode" => $this->sBotCode ?? '',
|
||||
"sChatCode" => $this->sChatCode ?? '',
|
||||
"sBotToken" => $this->sBotToken ?? '',
|
||||
"iChatId" => $this->iChatId ?? '',
|
||||
"sPhotoUrl" => $this->sPhotoUrl ?? '',
|
||||
"iPhotoWidth" => $this->iPhotoWidth,
|
||||
"iPhotoHeight" => $this->iPhotoHeight,
|
||||
"sFileUrl" => $this->sFileUrl,
|
||||
"sFileName" => $this->sFileName,
|
||||
"sMsg" => $this->sMsg ?? '',
|
||||
"sType" => $this->sType ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
215
app/Services/TomTool/Telegram/Sdk.php
Executable file
215
app/Services/TomTool/Telegram/Sdk.php
Executable file
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool\Telegram;
|
||||
use Telegram\Bot\Api;
|
||||
use Telegram\Bot\FileUpload\InputFile;
|
||||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use App\Services\TomTool\Flow;
|
||||
//use Intervention\Image\Laravel\Facades\Image;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Intervention\Image\Drivers\Gd\Driver;
|
||||
use App\Services\TomTool\HttpV2;
|
||||
|
||||
class Sdk
|
||||
{
|
||||
public $sMsgFormat = [];
|
||||
public $sMsgFormatType = 'none';
|
||||
public $sBotToken = '';
|
||||
public $iChatId = 0;
|
||||
public $sPhotoUrl = '';
|
||||
public $iPhotoWidth = 0;
|
||||
public $iPhotoHeight = 0;
|
||||
public $sFileUrl = '';
|
||||
public $sFileName = '';
|
||||
public $sMsg;
|
||||
public $oApi;
|
||||
|
||||
private const ALLOWED_FORMATS = ['none', 'html', 'markdown', 'markdownV2'];
|
||||
|
||||
public static function start()
|
||||
{
|
||||
$oInstance = new self();
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function setBotToken($sBotToken)
|
||||
{
|
||||
$this->oApi = new Api($sBotToken);
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatId($iChatId)
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMsgS($sMsg = '')
|
||||
{
|
||||
$this->sMsg = $sMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhotoUrl($sUrl = '', $iPhotoWidth = 0, $iPhotoHeight = 0)
|
||||
{
|
||||
$this->sPhotoUrl = $sUrl;
|
||||
$this->iPhotoWidth = $iPhotoWidth;
|
||||
$this->iPhotoHeight = $iPhotoHeight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFileUrl($sFileUrl = '', $sFileName = '')
|
||||
{
|
||||
$this->sFileUrl = $sFileUrl;
|
||||
$this->sFileName = $sFileName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType($sType)
|
||||
{
|
||||
if ($sType) {
|
||||
$this->sMsgFormatType = $sType;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send()
|
||||
{
|
||||
$oFlow = Flow::start("tg_sdk_send");
|
||||
|
||||
$this->msgFormat();
|
||||
|
||||
// try {
|
||||
|
||||
if ($this->sPhotoUrl) {
|
||||
$r = $this->oApi->sendPhoto($this->sMsgFormat);
|
||||
} else if ($this->sFileUrl) {
|
||||
$r = $this->oApi->sendDocument($this->sMsgFormat);
|
||||
} else {
|
||||
$r = $this->oApi->sendMessage($this->sMsgFormat);
|
||||
}
|
||||
|
||||
if (!($r && $r->getMessageId())) {
|
||||
return $oFlow->fail($r);
|
||||
}
|
||||
// } catch (TelegramSDKException $e) {
|
||||
// return $oFlow->fail(['error' => $e->getMessage()]);
|
||||
// }
|
||||
|
||||
return $oFlow->done();
|
||||
}
|
||||
|
||||
public function getMsgFormat()
|
||||
{
|
||||
$this->msgFormat();
|
||||
|
||||
return $this->sMsgFormat;
|
||||
}
|
||||
|
||||
public function msgFormat()
|
||||
{
|
||||
if (!in_array($this->sMsgFormatType, self::ALLOWED_FORMATS, true)) {
|
||||
throw new \RuntimeException("不支持的格式类型: {$this->sMsgFormatType}");
|
||||
}
|
||||
|
||||
$method = 'msgFormat_' . $this->sMsgFormatType;
|
||||
$this->sMsgFormat = $this->$method();
|
||||
|
||||
if ($this->sPhotoUrl) {
|
||||
|
||||
$oManager = new ImageManager(new Driver());
|
||||
|
||||
$sTempPath = $_ENV['dir_base'].'public/upload';
|
||||
$sTempPath .= "/tg_output.png";
|
||||
|
||||
$sImg = file_get_contents($this->sPhotoUrl);
|
||||
$oImage = $oManager->read($sImg);
|
||||
|
||||
if ($this->iPhotoWidth && $this->iPhotoHeight) {
|
||||
$oImage = $oImage->cover($this->iPhotoWidth, $this->iPhotoHeight);
|
||||
}
|
||||
|
||||
$oImage->toJpg(80)->save($sTempPath);
|
||||
|
||||
$photo = InputFile::create($sTempPath, date("Y-m-d").'.jpg');
|
||||
|
||||
$this->sMsgFormat["photo"] = $photo;
|
||||
|
||||
$this->sMsgFormat["caption"] = $this->sMsgFormat["text"];
|
||||
unset($this->sMsgFormat["text"]);
|
||||
|
||||
} else if ($this->sFileUrl) {
|
||||
|
||||
if (!$this->sFileName) {
|
||||
$sFileName = date("Y-m-d H:i:s").'.txt';
|
||||
} else {
|
||||
$sFileName = $this->sFileName;
|
||||
}
|
||||
|
||||
$sFile = InputFile::create($this->sFileUrl, $sFileName);
|
||||
|
||||
$this->sMsgFormat["document"] = $sFile;
|
||||
$this->sMsgFormat["caption"] = $this->sMsgFormat["text"];
|
||||
unset($this->sMsgFormat["text"]);
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function msgFormat_none()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function msgFormat_html()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => strip_tags($this->sMsg, [
|
||||
'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike', 'del', 'span', 'tg-spoiler', 'a', 'tg-emoji',
|
||||
'code', 'pre',
|
||||
]),
|
||||
'parse_mode' => 'HTML',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function msgFormat_markdown()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => 'Markdown',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function msgFormat_markdownV2()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => 'MarkdownV2',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
361
app/Services/TomTool/Telegram/Slave.php
Executable file
361
app/Services/TomTool/Telegram/Slave.php
Executable file
@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
// 版本 25/12/12
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool\Telegram;
|
||||
|
||||
use App\Services\TomTool\HttpV2;
|
||||
use App\Services\TomTool\Telegram\Sdk as ServiceTelegramSdk;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Services\HttpQueue as ServiceHttpQueue;
|
||||
|
||||
/**
|
||||
|
||||
提醒:
|
||||
master自身已经没有队列了,收到slave消息直接转发
|
||||
|
||||
默认:
|
||||
走本地队列,发送到master
|
||||
走异步
|
||||
|
||||
new:
|
||||
start()
|
||||
log()
|
||||
warn()
|
||||
fail()
|
||||
notify()
|
||||
|
||||
opt:
|
||||
enableMaster(:true)
|
||||
enableMasterQueue(:true)
|
||||
enableSlaveQueue(:true)
|
||||
enableDetail(:true)
|
||||
|
||||
set:
|
||||
setMsgS()
|
||||
setMsgA()
|
||||
setBotCode()
|
||||
setChatCode()
|
||||
setBotToken()
|
||||
setChatId()
|
||||
|
||||
action:
|
||||
send()
|
||||
sendMaster()
|
||||
sendLocal()
|
||||
|
||||
*/
|
||||
|
||||
class Slave
|
||||
{
|
||||
public string $sBotCode;
|
||||
public string $sChatCode;
|
||||
|
||||
public string $sBotToken = '';
|
||||
public int $iChatId = 0;
|
||||
|
||||
public string $sMsg = '';
|
||||
public string $sPhotoUrl = '';
|
||||
public int $iPhotoWidth = 0;
|
||||
public int $iPhotoHeight = 0;
|
||||
|
||||
public string $sFileUrl = '';
|
||||
public string $sFileName = '';
|
||||
|
||||
public string $sType = '';
|
||||
|
||||
public bool $enableMaster = true;
|
||||
public bool $enableMasterQueue = false; // 这里之前想错了,不会有master队列,只在slave有,master转发就行了
|
||||
public bool $enableSlaveQueue = false;
|
||||
|
||||
public bool $enableDetail = true;
|
||||
|
||||
public bool $enableAsync = false;
|
||||
|
||||
private bool $enableSend = true;
|
||||
|
||||
// new
|
||||
public static function start(string $sBotCode = 'base', string $sChatCode = 'base'): self
|
||||
{
|
||||
$oInstance = new self;
|
||||
$oInstance->sBotCode = $sBotCode;
|
||||
$oInstance->sChatCode = $sChatCode;
|
||||
$oInstance->enableSend = filter_var($_ENV["tg_send_enable"] ?? true, FILTER_VALIDATE_BOOLEAN);
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
// new
|
||||
public static function log(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("log", $sChatCode);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function warn(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("warn", $sChatCode);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function fail(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("fail", $sChatCode);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function notify(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("fail", $sChatCode);
|
||||
}
|
||||
|
||||
|
||||
// opt
|
||||
public function enableAsync(bool $b = true): self
|
||||
{
|
||||
$this->enableAsync = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableDetail(bool $b = true): self
|
||||
{
|
||||
$this->enableDetail = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableSlaveQueue(bool $b = true): self
|
||||
{
|
||||
$this->enableSlaveQueue = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableMaster(bool $b = true): self
|
||||
{
|
||||
$this->enableMaster = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableMasterQueue(bool $b = true): self
|
||||
{
|
||||
$this->enableMasterQueue = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setPhotoUrl(string $sPhotoUrl, int $iWidth = 0, int $iHeight = 0): self
|
||||
{
|
||||
$this->sPhotoUrl = $sPhotoUrl;
|
||||
$this->iPhotoWidth = $iWidth;
|
||||
$this->iPhotoHeight = $iHeight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setFileUrl(string $sFileUrl, string $sFileName = ''): self
|
||||
{
|
||||
$this->sFileUrl = $sFileUrl;
|
||||
$this->sFileName = $sFileName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setType(string $sType): self
|
||||
{
|
||||
$this->sType = $sType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setMsgS(string $sMsg = ''): self
|
||||
{
|
||||
if ($this->enableDetail) {
|
||||
$sMsg = "[ ".$this->getEnvSiteCode()." | ".date("d:H:i:s")." ] ".$sMsg;
|
||||
}
|
||||
|
||||
$this->sMsg = $sMsg;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setMsgA(array $aMsg = []): self
|
||||
{
|
||||
$aMsgNew = $aMsg;
|
||||
|
||||
if ($this->enableDetail) {
|
||||
$aMsgNew = [];
|
||||
$aMsgNew["site_code"] = $this->getEnvSiteCode();
|
||||
$aMsgNew["date"] = date("Y-m-d H:i:s");
|
||||
$aMsgNew["data"] = $aMsg;
|
||||
}
|
||||
|
||||
$this->sMsg = json_encode($aMsgNew, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setBotCode(string $sBotCode): self
|
||||
{
|
||||
$this->sBotCode = $sBotCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setChatCode(string $sChatCode): self
|
||||
{
|
||||
$this->sChatCode = $sChatCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setBotToken(string $sBotToken): self
|
||||
{
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setChatId(int $iChatId): self
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// action
|
||||
public function send($xMsg = ''): Flow
|
||||
{
|
||||
if ($this->enableSend == false) {
|
||||
$oFlow = Flow::start();
|
||||
return $oFlow->done("不发送");
|
||||
}
|
||||
|
||||
if ($xMsg) {
|
||||
if (is_array($xMsg)) {
|
||||
$this->setMsgA($xMsg);
|
||||
} else {
|
||||
$this->setMsgS($xMsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->sMsg) {
|
||||
// throw new \Exception("未设置msg");
|
||||
}
|
||||
|
||||
if ($this->enableMaster) {
|
||||
$oFlow = $this->_send_master();
|
||||
} else {
|
||||
$oFlow = $this->_send_local();
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
// action
|
||||
public function sendMaster($xMsg = ''): Flow
|
||||
{
|
||||
$this->enableMaster(true);
|
||||
return $this->send($xMsg);
|
||||
}
|
||||
|
||||
// action
|
||||
public function sendLocal($xMsg = ''): Flow
|
||||
{
|
||||
$this->enableMaster(false);
|
||||
return $this->send($xMsg);
|
||||
}
|
||||
|
||||
private function _send_master(): Flow
|
||||
{
|
||||
$aData = $this->buildDataTom();
|
||||
|
||||
if ($this->enableSlaveQueue) {
|
||||
$oFlow = ServiceHttpQueue::start("telegram_master")->arg($aData)->save();
|
||||
} else {
|
||||
|
||||
$oFlow = Flow::start("_send_master");
|
||||
$sUrlMaSterBase = $_ENV["url_master_base"] ?? "";
|
||||
|
||||
if (!$sUrlMaSterBase) {
|
||||
throw new \Exception("未设置env的url_master_base");
|
||||
}
|
||||
|
||||
$sUrlMasterPost = "https://".$sUrlMaSterBase."/api/telegram/post";
|
||||
|
||||
try {
|
||||
|
||||
$oHttpV2 = HttpV2::make($sUrlMasterPost);
|
||||
|
||||
if ($this->enableAsync) {
|
||||
$oHttpV2 = $oHttpV2->enableAsync();
|
||||
}
|
||||
|
||||
$r = $oHttpV2->enableJsonAs()->setData($aData)->send();
|
||||
|
||||
if ($this->enableAsync) {
|
||||
return $oFlow->sData("async")->done("async");
|
||||
} else {
|
||||
$a = json_decode($r, true);
|
||||
$isDone = $a["isDone"] ?? false;
|
||||
|
||||
if ($isDone) {
|
||||
return $oFlow->done();
|
||||
} else {
|
||||
return $oFlow->fail($r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$oFlow = $oFlow->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
// 直接从本地发到tg,几乎用不上,都走master了
|
||||
private function _send_local(): Flow
|
||||
{
|
||||
if ($this->enableSlaveQueue) {
|
||||
$aData = $this->buildDataTom();
|
||||
$oFlow = ServiceHttpQueue::start("telegram_local")->arg($aData)->save();
|
||||
} else {
|
||||
$oFlow = ServiceTelegramSdk::start($this->sBotToken, $this->iChatId)->setMsgS($this->sMsg)->send();
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
private function buildDataTom(): array
|
||||
{
|
||||
return [
|
||||
"sBotCode" => $this->sBotCode ?? '',
|
||||
"sChatCode" => $this->sChatCode ?? '',
|
||||
"sBotToken" => $this->sBotToken ?? '',
|
||||
"iChatId" => $this->iChatId ?? '',
|
||||
"enableMasterQueue" => $this->enableMasterQueue ?? '',
|
||||
"enableMaster" => $this->enableMaster,
|
||||
"sMsg" => $this->sMsg ?? '',
|
||||
"sPhotoUrl" => $this->sPhotoUrl ?? '',
|
||||
"iPhotoWidth" => $this->iPhotoWidth ?? 0,
|
||||
"iPhotoHeight" => $this->iPhotoHeight ?? 0,
|
||||
"sType" => $this->sType ?? '',
|
||||
"sFileUrl" => $this->sFileUrl ?? '',
|
||||
"sFileName" => $this->sFileName ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
private function getEnvSiteCode(): string
|
||||
{
|
||||
$sEnvSiteCode = $_ENV["site_code"] ?? "未设置env的site_code";
|
||||
|
||||
return $sEnvSiteCode;
|
||||
}
|
||||
|
||||
}
|
||||
121
app/Services/TomTool/TelegramV2.php
Executable file
121
app/Services/TomTool/TelegramV2.php
Executable file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\IM;
|
||||
|
||||
use App\Models\Config;
|
||||
use Telegram\Bot\Api;
|
||||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use function strip_tags;
|
||||
|
||||
// 没有必须,默认使用base配置
|
||||
final class TelegramV2
|
||||
{
|
||||
private Api $bot;
|
||||
public $iTo;
|
||||
public $sToken;
|
||||
public $sMsgType = false;
|
||||
|
||||
public function __construct($sToken = "base", $sTo = "base")
|
||||
{
|
||||
$this->setToken($sToken);
|
||||
|
||||
$this->setTo($sTo);
|
||||
|
||||
// $this->bot = new Api($this->sToken);
|
||||
}
|
||||
|
||||
public function setMsgType($sMsgType)
|
||||
{
|
||||
$this->sMsgType = $sMsgType;
|
||||
}
|
||||
|
||||
public function setTo($xTo, $sType = "code")
|
||||
{
|
||||
$iChatId = $xTo;
|
||||
|
||||
if ($sType == "code") {
|
||||
// $sEnvKey = "tgChat_".$xTo;
|
||||
$iChatId = $_ENV['tgChat'][$iChatId];
|
||||
}
|
||||
|
||||
$this->iTo = $iChatId;
|
||||
}
|
||||
|
||||
public function setToken($xToken, $sType = "code")
|
||||
{
|
||||
$sToken = $xToken;
|
||||
|
||||
if ($sType == "code") {
|
||||
// $sEnvKey = "tgToken_".$xToken;
|
||||
$sToken = $_ENV['tgToken'][$sToken];
|
||||
}
|
||||
|
||||
$this->sToken = $sToken;
|
||||
|
||||
$this->bot = new Api($this->sToken);
|
||||
}
|
||||
|
||||
public function send($sMsg, $sMsgType = 'str')
|
||||
{
|
||||
if ($sMsgType == "arr") {
|
||||
$sMsg = json_encode($sMsg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
if ($this->sMsgType === false) {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else if ($this->sMsgType === "html") {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => strip_tags(
|
||||
$sMsg,
|
||||
['b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike','del', 'span','tg-spoiler', 'a', 'tg-emoji',
|
||||
'code', 'pre',
|
||||
]
|
||||
),
|
||||
'parse_mode' => 'HTML',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else if ($this->sMsgType === "markdown") {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => 'Markdown',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else if ($this->sMsgType === "markdownV2") {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => 'MarkdownV2',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->bot->sendMessage($sendMessage);
|
||||
}
|
||||
|
||||
}
|
||||
362
app/Services/TomTool/TelegramVdb_rengle.php
Executable file
362
app/Services/TomTool/TelegramVdb_rengle.php
Executable file
@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use App\Services\HttpQueue as ServiceHttpQueue;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Models\TelegramKey as ModelTelegramKey;
|
||||
use Telegram\Bot\Api;
|
||||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use function strip_tags;
|
||||
|
||||
/**
|
||||
|
||||
更新:
|
||||
data会save清空,不再属于set
|
||||
|
||||
new:
|
||||
TelegramVdb::make(?$botCode, ?$chatCode)
|
||||
|
||||
快速:
|
||||
TelegramVdb::make()->save($sData)
|
||||
|
||||
进阶:
|
||||
TelegramVdb::make()->dataArr($aData)->setFormat("html")->save();
|
||||
TelegramVdb::make()->setFormat()->data($xData)->toJson()->save();
|
||||
|
||||
data:
|
||||
data[Arr/Str/Json]($data)
|
||||
data($aData)->toJson()
|
||||
|
||||
action:
|
||||
save()
|
||||
|
||||
set:
|
||||
setChatCode($code)
|
||||
setBotCode($code)
|
||||
setChatId($id) 覆盖code
|
||||
setBotToken($token) 覆盖code
|
||||
setFormat($type)
|
||||
enableDate()
|
||||
|
||||
*/
|
||||
|
||||
final class TelegramVdb
|
||||
{
|
||||
public $sBotCode;
|
||||
public $sChatCode;
|
||||
public $sBotToken = null;
|
||||
public $iChatId = null;
|
||||
public $sMsg = '';
|
||||
public $sMsgFormat = '';
|
||||
public $sMsgFormatType = 'none';
|
||||
public $bEnableDate = false;
|
||||
private $bKeyBegin = false;
|
||||
public $sClient = "master";
|
||||
|
||||
private const ALLOWED_FORMATS = ['none', 'html', 'markdown', 'markdownV2'];
|
||||
|
||||
public static function log()
|
||||
{
|
||||
return self::make("log", "admin");
|
||||
}
|
||||
|
||||
public static function warn()
|
||||
{
|
||||
return self::make("warn", "admin");
|
||||
}
|
||||
|
||||
public static function fail()
|
||||
{
|
||||
return self::make("fail", "admin");
|
||||
}
|
||||
|
||||
public static function notify()
|
||||
{
|
||||
return self::make("notify", "admin");
|
||||
}
|
||||
|
||||
public static function make($sBotCode = "base", $sChatCode = "base")
|
||||
{
|
||||
$oInstance = new self();
|
||||
$oInstance->sBotCode = $sBotCode;
|
||||
$oInstance->sChatCode = $sChatCode;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function send($sMsg = '')
|
||||
{
|
||||
return $this->_ok($sMsg, true);
|
||||
}
|
||||
|
||||
public function save($sMsg = '')
|
||||
{
|
||||
return $this->_ok($sMsg);
|
||||
}
|
||||
|
||||
private function sendNow()
|
||||
{
|
||||
$oResult = Flow::make("send");
|
||||
$oBot = new Api($this->sBotToken);
|
||||
|
||||
$r = $oBot->sendMessage($this->sMsgFormat);
|
||||
|
||||
if ($r->isOk()){
|
||||
return $oResult->done();
|
||||
} else {
|
||||
return $oResult->fail("send失败");
|
||||
}
|
||||
}
|
||||
|
||||
private function HttpQueueSave()
|
||||
{
|
||||
$aTelegramArg = [
|
||||
"bot_token" => $this->sBotToken,
|
||||
"chat_id" => $this->iChatId,
|
||||
"msg" => $this->sMsgFormat,
|
||||
];
|
||||
|
||||
$aReturn = ServiceHttpQueue::start("telegram")->arg($aTelegramArg)->save()->getResult();
|
||||
|
||||
return $aReturn;
|
||||
}
|
||||
|
||||
public function _ok($sMsg = '', $bSendNow = false)
|
||||
{
|
||||
$bTgSendEnable = $_ENV["tg_send_enable"] ?? false;
|
||||
if (!$bTgSendEnable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oResult = Flow::make("save");
|
||||
|
||||
$this->keyBegin();
|
||||
|
||||
if ($sMsg) {
|
||||
if (is_array($sMsg)) {
|
||||
$this->aData($sMsg);
|
||||
} else {
|
||||
$this->sData($sMsg);
|
||||
}
|
||||
}
|
||||
|
||||
$this->msgFormat();
|
||||
|
||||
if ($bSendNow) {
|
||||
$aReturn = $this->sendNow();
|
||||
} else {
|
||||
$aReturn = $this->HttpQueueSave();
|
||||
}
|
||||
|
||||
$this->sMsg = '';
|
||||
|
||||
return $aReturn;
|
||||
}
|
||||
|
||||
public function enableDate($b = true)
|
||||
{
|
||||
$this->bEnableDate = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function data($xData)
|
||||
{
|
||||
$this->sMsg = $xData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function jData($sMsg = '')
|
||||
{
|
||||
$this->dataJson($sMsg);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function aData($sMsg = [])
|
||||
{
|
||||
$this->dataArr($sMsg);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sData($sMsg = '')
|
||||
{
|
||||
$this->dataStr($sMsg);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toJson()
|
||||
{
|
||||
$this->withDateArr();
|
||||
$this->sMsg = json_encode($this->sMsg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function dataStr($sMsg = '')
|
||||
{
|
||||
$this->withDateStr();
|
||||
$this->sMsg = $sMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function dataArr($sMsg = [])
|
||||
{
|
||||
$this->withDateArr();
|
||||
$this->sMsg = json_encode($sMsg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function dataJson($sMsg = '')
|
||||
{
|
||||
$this->dataStr($sMsg);
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function withDateArr()
|
||||
{
|
||||
if ($this->bEnableDate) {
|
||||
$this->sMsg["date"] = date("Y-m-d H:i:s");
|
||||
}
|
||||
}
|
||||
|
||||
private function withDateStr()
|
||||
{
|
||||
if ($this->bEnableDate) {
|
||||
$this->sMsg = '['.date("Y-m-d H:i:s").'] '.$this->sMsg;
|
||||
}
|
||||
}
|
||||
|
||||
public function setFormat($sMsgFormatType = "none")
|
||||
{
|
||||
$this->sMsgFormatType = $sMsgFormatType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBotCode($sBotCode)
|
||||
{
|
||||
$this->sBotCode = $sBotCode;
|
||||
$this->bKeyBegin = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatCode($sChatCode)
|
||||
{
|
||||
$this->sChatCode = $sChatCode;
|
||||
$this->bKeyBegin = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBotToken($sBotToken)
|
||||
{
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatId($iChatId)
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function keyBegin()
|
||||
{
|
||||
if (!$this->bKeyBegin) {
|
||||
if (!$this->sBotToken) {
|
||||
$this->sBotToken = $this->botTokenByCode();
|
||||
}
|
||||
|
||||
if (!$this->iChatId) {
|
||||
$this->iChatId = $this->chatIdByCode();
|
||||
}
|
||||
|
||||
if (!$this->sBotToken || !$this->iChatId) {
|
||||
throw new \RuntimeException("botToken或chatId没找到,botCode={$this->sBotCode},chatCode={$this->sChatCode}");
|
||||
}
|
||||
|
||||
$this->bKeyBegin = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function msgFormat()
|
||||
{
|
||||
if (!in_array($this->sMsgFormatType, self::ALLOWED_FORMATS, true)) {
|
||||
throw new \RuntimeException("不支持的格式类型: {$this->sMsgFormatType}");
|
||||
}
|
||||
|
||||
$method = 'msgFormat_' . $this->sMsgFormatType;
|
||||
$this->$method();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function botTokenByCode()
|
||||
{
|
||||
return ModelTelegramKey::botTokenByCode($this->sBotCode);
|
||||
}
|
||||
|
||||
private function chatIdByCode()
|
||||
{
|
||||
return ModelTelegramKey::chatIdByCode($this->sChatCode);
|
||||
}
|
||||
|
||||
private function msgFormat_none()
|
||||
{
|
||||
$this->sMsgFormat = [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function msgFormat_html()
|
||||
{
|
||||
$this->sMsgFormat = [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => strip_tags($this->sMsg, [
|
||||
'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike', 'del', 'span', 'tg-spoiler', 'a', 'tg-emoji',
|
||||
'code', 'pre',
|
||||
]),
|
||||
'parse_mode' => 'HTML',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function msgFormat_markdown()
|
||||
{
|
||||
$this->sMsgFormat = [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => 'Markdown',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function msgFormat_markdownV2()
|
||||
{
|
||||
$this->sMsgFormat = [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => 'MarkdownV2',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
130
app/Services/TomTool/ThrowableHandler.php
Executable file
130
app/Services/TomTool/ThrowableHandler.php
Executable file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use Throwable as ThrowableBase;
|
||||
use Illuminate\Database\QueryException;
|
||||
use App\Services\TomTool\TelegramVdb;
|
||||
|
||||
/**
|
||||
|
||||
new:
|
||||
$oThrowableHandler = ThrowableHandler::make($e);
|
||||
|
||||
快速:
|
||||
$aResult = ThrowableHandler::make($e)->toArr();
|
||||
|
||||
action:
|
||||
fetch() or get()
|
||||
|
||||
set:
|
||||
enableTg()
|
||||
enableTrace()
|
||||
setMaxDepth()
|
||||
|
||||
*/
|
||||
|
||||
class ThrowableHandler
|
||||
{
|
||||
protected bool $tgEnable = false;
|
||||
protected bool $showTrace = false;
|
||||
protected int $maxDepth = 3;
|
||||
protected ThrowableBase $e;
|
||||
|
||||
public static function make(ThrowableBase $e): self
|
||||
{
|
||||
$oInstance = new self();
|
||||
$oInstance->e = $e;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function fetch()
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$data = $this->buildExceptionData($this->e, 0);
|
||||
|
||||
if ($this->tgEnable) {
|
||||
$this->sendTelegram($data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function toArr(): array
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->get(), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function enableTg(bool $enable = true): self
|
||||
{
|
||||
$this->tgEnable = $enable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableTrace(bool $enable = true): self
|
||||
{
|
||||
$this->showTrace = $enable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxDepth(int $depth): self
|
||||
{
|
||||
$this->maxDepth = max(1, $depth);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function buildExceptionData(ThrowableBase $e, int $depth): array
|
||||
{
|
||||
$data = [
|
||||
'message' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
];
|
||||
|
||||
$code = $e->getCode();
|
||||
if ($code !== null && $code !== 0) {
|
||||
$data['code'] = $code;
|
||||
}
|
||||
|
||||
if ($this->showTrace) {
|
||||
$filteredTrace = array_filter($e->getTrace(), function ($frame) {
|
||||
return empty($frame['file']) || !str_contains($frame['file'], '/vendor/');
|
||||
});
|
||||
|
||||
$traceLines = [];
|
||||
foreach ($filteredTrace as $frame) {
|
||||
$file = $frame['file'] ?? '[internal]';
|
||||
$line = $frame['line'] ?? '';
|
||||
$func = isset($frame['class'], $frame['function'])
|
||||
? "{$frame['class']}{$frame['type']}{$frame['function']}"
|
||||
: ($frame['function'] ?? '[unknown]');
|
||||
$traceLines[] = "{$file}:{$line} {$func}()";
|
||||
}
|
||||
|
||||
$data['trace'] = implode("\n", $traceLines);
|
||||
}
|
||||
|
||||
if ($depth < $this->maxDepth && $e->getPrevious() instanceof ThrowableBase) {
|
||||
$data['previous'] = $this->buildExceptionData($e->getPrevious(), $depth + 1);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function sendTelegram(array $data): void
|
||||
{
|
||||
TelegramVdb::make()->aData($data)->save();
|
||||
}
|
||||
}
|
||||
|
||||
21
app/Services/TomTool/Tom.php
Executable file
21
app/Services/TomTool/Tom.php
Executable file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
class Tom
|
||||
{
|
||||
public static function toJson($aData)
|
||||
{
|
||||
return json_encode($aData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
public static function toJsonBr($aData)
|
||||
{
|
||||
$sData = self::toJson($aData);
|
||||
$sData = str_replace("\\n", "<br>", $sData);
|
||||
|
||||
return $sData;
|
||||
}
|
||||
}
|
||||
18
artisan
Executable file
18
artisan
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the command...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
$status = $app->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
||||
25
bootstrap/app.php
Executable file
25
bootstrap/app.php
Executable file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->prepend(\Spatie\ResponseCache\Middlewares\CacheResponse::class);
|
||||
$middleware->alias([
|
||||
'doCache' => \Spatie\ResponseCache\Middlewares\CacheResponse::class,
|
||||
]);
|
||||
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
'api/*',
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
})->create();
|
||||
2
bootstrap/cache/.gitignore
vendored
Executable file
2
bootstrap/cache/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
7
bootstrap/providers.php
Executable file
7
bootstrap/providers.php
Executable file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\NavServiceProvider::class,
|
||||
App\Providers\ArticleOrderLikesProvider::class,
|
||||
];
|
||||
91
composer.json
Executable file
91
composer.json
Executable file
@ -0,0 +1,91 @@
|
||||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": ["laravel", "framework"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"http-interop/http-factory-guzzle": "^1.2",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/scout": "^10.23",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"meilisearch/meilisearch-php": "^1.16",
|
||||
"overtrue/pinyin": "~5.0",
|
||||
"spatie/laravel-responsecache": "^7.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/pail": "^1.2.2",
|
||||
"laravel/pint": "^1.24",
|
||||
"laravel/sail": "^1.41",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"phpunit/phpunit": "^11.5.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"setup": [
|
||||
"composer install",
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
|
||||
"@php artisan key:generate",
|
||||
"@php artisan migrate --force",
|
||||
"npm install",
|
||||
"npm run build"
|
||||
],
|
||||
"dev": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
|
||||
],
|
||||
"test": [
|
||||
"@php artisan config:clear --ansi",
|
||||
"@php artisan test"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi",
|
||||
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
|
||||
"@php artisan migrate --graceful --ansi"
|
||||
],
|
||||
"pre-package-uninstall": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
8969
composer.lock
generated
Executable file
8969
composer.lock
generated
Executable file
File diff suppressed because it is too large
Load Diff
123
config/app.php
Executable file
123
config/app.php
Executable file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'is_loc' => env('is_loc', false),
|
||||
'page_limit' => env('page_limit', 10),
|
||||
|
||||
'name' => env('APP_NAME', '网站名字'),
|
||||
'site_code' => env('site_code', 'site_code'),
|
||||
'site_code_short' => env('site_code_short', 'site_code_short'),
|
||||
'title' => env('app_title', '网站标题'),
|
||||
'keywords' => env('app_keywords', '关键词'),
|
||||
'desc' => env('app_desc', '页面简介'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. The timezone
|
||||
| is set to "UTC" by default as it is suitable for most use cases.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by Laravel's translation / localization methods. This option can be
|
||||
| set to any locale for which you plan to have translation strings.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is utilized by Laravel's encryption services and should be set
|
||||
| to a random, 32 character string to ensure that all encrypted values
|
||||
| are secure. You should do this prior to deploying the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'previous_keys' => [
|
||||
...array_filter(
|
||||
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
|
||||
),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
];
|
||||
115
config/auth.php
Executable file
115
config/auth.php
Executable file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default authentication "guard" and password
|
||||
| reset "broker" for your application. You may change these values
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => env('AUTH_GUARD', 'web'),
|
||||
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| which utilizes session storage plus the Eloquent user provider.
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| providers to represent the model / table. These providers may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options specify the behavior of Laravel's password
|
||||
| reset functionality, including the table utilized for token storage
|
||||
| and the user provider that is invoked to actually retrieve users.
|
||||
|
|
||||
| The expiry time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the number of seconds before a password confirmation
|
||||
| window expires and users are asked to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||
|
||||
];
|
||||
117
config/cache.php
Executable file
117
config/cache.php
Executable file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache store that will be used by the
|
||||
| framework. This connection is utilized if another isn't explicitly
|
||||
| specified when running a cache operation inside the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_STORE', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "array", "database", "file", "memcached",
|
||||
| "redis", "dynamodb", "octane",
|
||||
| "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_CACHE_CONNECTION'),
|
||||
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'stores' => [
|
||||
'database',
|
||||
'array',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||
| stores, there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
|
||||
|
||||
];
|
||||
8
config/constants.php
Executable file
8
config/constants.php
Executable file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'cache_ttl' => [
|
||||
'one_week' => 60 * 60 * 24 * 7,
|
||||
'one_day' => 60 * 60 * 24,
|
||||
],
|
||||
];
|
||||
183
config/database.php
Executable file
183
config/database.php
Executable file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for database operations. This is
|
||||
| the connection which will be utilized unless another connection
|
||||
| is explicitly specified when you execute a query / statement.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below are all of the database connections defined for your application.
|
||||
| An example configuration is provided for each database system which
|
||||
| is supported by Laravel. You're free to add / remove connections.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
'busy_timeout' => null,
|
||||
'journal_mode' => null,
|
||||
'synchronous' => null,
|
||||
'transaction_mode' => 'DEFERRED',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'mariadb' => [
|
||||
'driver' => 'mariadb',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => env('DB_SSLMODE', 'prefer'),
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '1433'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run on the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as Memcached. You may define your connection settings here.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
|
||||
'persistent' => env('REDIS_PERSISTENT', false),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
96
config/filesystems.php
Executable file
96
config/filesystems.php
Executable file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. The "local" disk, as well as a variety of cloud
|
||||
| based disks are available to your application for file storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below you may configure as many filesystem disks as necessary, and you
|
||||
| may even configure multiple disks for the same driver. Examples for
|
||||
| most supported storage drivers are configured here for reference.
|
||||
|
|
||||
| Supported drivers: "local", "ftp", "sftp", "s3"
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/private'),
|
||||
'serve' => true,
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => rtrim(env('APP_URL'), '/').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
'upload' => [
|
||||
'driver' => 'local',
|
||||
'root' => public_path('upload'),
|
||||
'url' => env('APP_URL').'/upload',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
'upload_images' => [
|
||||
'driver' => 'local',
|
||||
'root' => public_path('upload/images'),
|
||||
'url' => env('APP_URL').'/upload/images',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the symbolic links that will be created when the
|
||||
| `storage:link` Artisan command is executed. The array keys should be
|
||||
| the locations of the links and the values should be their targets.
|
||||
|
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
|
||||
];
|
||||
132
config/logging.php
Executable file
132
config/logging.php
Executable file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default log channel that is utilized to write
|
||||
| messages to your logs. The value provided here should match one of
|
||||
| the channels present in the list of "channels" configured below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log channels for your application. Laravel
|
||||
| utilizes the Monolog PHP logging library, which includes a variety
|
||||
| of powerful log handlers and formatters that you're free to use.
|
||||
|
|
||||
| Available drivers: "single", "daily", "slack", "syslog",
|
||||
| "errorlog", "monolog", "custom", "stack"
|
||||
|
|
||||
*/
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
|
||||
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||
'level' => env('LOG_LEVEL', 'critical'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_URL'),
|
||||
'port' => env('PAPERTRAIL_PORT'),
|
||||
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'handler_with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
118
config/mail.php
Executable file
118
config/mail.php
Executable file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Mailer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default mailer that is used to send all email
|
||||
| messages unless another mailer is explicitly specified when sending
|
||||
| the message. All additional mailers can be configured within the
|
||||
| "mailers" array. Examples of each type of mailer are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'log'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailer Configurations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure all of the mailers used by your application plus
|
||||
| their respective settings. Several examples have been configured for
|
||||
| you and you are free to add your own as your application requires.
|
||||
|
|
||||
| Laravel supports a variety of mail "transport" drivers that can be used
|
||||
| when delivering an email. You may specify which one you're using for
|
||||
| your mailers below. You may also add additional mailers if needed.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||
| "postmark", "resend", "log", "array",
|
||||
| "failover", "roundrobin"
|
||||
|
|
||||
*/
|
||||
|
||||
'mailers' => [
|
||||
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'scheme' => env('MAIL_SCHEME'),
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||
'port' => env('MAIL_PORT', 2525),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'transport' => 'ses',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'transport' => 'postmark',
|
||||
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'transport' => 'resend',
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
'channel' => env('MAIL_LOG_CHANNEL'),
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
'roundrobin' => [
|
||||
'transport' => 'roundrobin',
|
||||
'mailers' => [
|
||||
'ses',
|
||||
'postmark',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all emails sent by your application to be sent from
|
||||
| the same address. Here you may specify a name and address that is
|
||||
| used globally for all emails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
|
||||
];
|
||||
8
config/path.php
Executable file
8
config/path.php
Executable file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'url_master_base' => env('url_master_base'),
|
||||
'url_resource_base' => env('url_resource_base'),
|
||||
|
||||
];
|
||||
129
config/queue.php
Executable file
129
config/queue.php
Executable file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel's queue supports a variety of backends via a single, unified
|
||||
| API, giving you convenient access to each backend using identical
|
||||
| syntax for each. The default queue connection is defined below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection options for every queue backend
|
||||
| used by your application. An example configuration is provided for
|
||||
| each backend supported by Laravel. You're also free to add more.
|
||||
|
|
||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
|
||||
| "deferred", "background", "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_QUEUE_CONNECTION'),
|
||||
'table' => env('DB_QUEUE_TABLE', 'jobs'),
|
||||
'queue' => env('DB_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
|
||||
'queue' => env('BEANSTALKD_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => 0,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
|
||||
'queue' => env('SQS_QUEUE', 'default'),
|
||||
'suffix' => env('SQS_SUFFIX'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'deferred' => [
|
||||
'driver' => 'deferred',
|
||||
],
|
||||
|
||||
'background' => [
|
||||
'driver' => 'background',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'connections' => [
|
||||
'database',
|
||||
'deferred',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Batching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following options configure the database and table that store job
|
||||
| batching information. These options can be updated to any database
|
||||
| connection and table which has been defined by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'batching' => [
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'job_batches',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control how and where failed jobs are stored. Laravel ships with
|
||||
| support for storing failed jobs in a simple file or in a database.
|
||||
|
|
||||
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
||||
97
config/responsecache.php
Executable file
97
config/responsecache.php
Executable file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* Determine if the response cache middleware should be enabled.
|
||||
*/
|
||||
'enabled' => env('RESPONSE_CACHE_ENABLED', true),
|
||||
|
||||
/*
|
||||
* The given class will determinate if a request should be cached. The
|
||||
* default class will cache all successful GET-requests.
|
||||
*
|
||||
* You can provide your own class given that it implements the
|
||||
* CacheProfile interface.
|
||||
*/
|
||||
'cache_profile' => Spatie\ResponseCache\CacheProfiles\CacheAllSuccessfulGetRequests::class,
|
||||
// 'cache_profile' => Spatie\ResponseCache\CacheProfiles\CacheAllRequests::class,
|
||||
// 'cache_profile' => \App\Support\CustomCacheProfile::class,
|
||||
//'cache_profile' => \App\Support\BrutalCacheProfile::class,
|
||||
|
||||
/*
|
||||
* Optionally, you can specify a header that will force a cache bypass.
|
||||
* This can be useful to monitor the performance of your application.
|
||||
*/
|
||||
'cache_bypass_header' => [
|
||||
'name' => env('CACHE_BYPASS_HEADER_NAME', null),
|
||||
'value' => env('CACHE_BYPASS_HEADER_VALUE', null),
|
||||
],
|
||||
|
||||
/*
|
||||
* When using the default CacheRequestFilter this setting controls the
|
||||
* default number of seconds responses must be cached.
|
||||
*/
|
||||
'cache_lifetime_in_seconds' => (int) env('RESPONSE_CACHE_LIFETIME', 60 * 60 * 24 * 7),
|
||||
|
||||
/*
|
||||
* This setting determines if a http header named with the cache time
|
||||
* should be added to a cached response. This can be handy when
|
||||
* debugging.
|
||||
*/
|
||||
'add_cache_time_header' => true,
|
||||
|
||||
/*
|
||||
* This setting determines the name of the http header that contains
|
||||
* the time at which the response was cached
|
||||
*/
|
||||
'cache_time_header_name' => env('RESPONSE_CACHE_HEADER_NAME', 'laravel-responsecache'),
|
||||
|
||||
/*
|
||||
* This setting determines if a http header named with the cache age
|
||||
* should be added to a cached response. This can be handy when
|
||||
* debugging.
|
||||
* ONLY works when "add_cache_time_header" is also active!
|
||||
*/
|
||||
'add_cache_age_header' => true,
|
||||
|
||||
/*
|
||||
* This setting determines the name of the http header that contains
|
||||
* the age of cache
|
||||
*/
|
||||
'cache_age_header_name' => env('RESPONSE_CACHE_AGE_HEADER_NAME', 'laravel-responsecache-age'),
|
||||
|
||||
/*
|
||||
* Here you may define the cache store that should be used to store
|
||||
* requests. This can be the name of any store that is
|
||||
* configured in app/config/cache.php
|
||||
*/
|
||||
'cache_store' => 'file',
|
||||
|
||||
/*
|
||||
* Here you may define replacers that dynamically replace content from the response.
|
||||
* Each replacer must implement the Replacer interface.
|
||||
*/
|
||||
'replacers' => [
|
||||
\Spatie\ResponseCache\Replacers\CsrfTokenReplacer::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* If the cache driver you configured supports tags, you may specify a tag name
|
||||
* here. All responses will be tagged. When clearing the responsecache only
|
||||
* items with that tag will be flushed.
|
||||
*
|
||||
* You may use a string or an array here.
|
||||
*/
|
||||
'cache_tag' => '',
|
||||
|
||||
/*
|
||||
* This class is responsible for generating a hash for a request. This hash
|
||||
* is used to look up a cached response.
|
||||
*/
|
||||
'hasher' => \Spatie\ResponseCache\Hasher\DefaultHasher::class,
|
||||
|
||||
/*
|
||||
* This class is responsible for serializing responses.
|
||||
*/
|
||||
'serializer' => \Spatie\ResponseCache\Serializers\DefaultSerializer::class,
|
||||
];
|
||||
218
config/scout.php
Executable file
218
config/scout.php
Executable file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Search Engine
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default search connection that gets used while
|
||||
| using Laravel Scout. This connection is used when syncing all models
|
||||
| to the search service. You should adjust this based on your needs.
|
||||
|
|
||||
| Supported: "algolia", "meilisearch", "typesense",
|
||||
| "database", "collection", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SCOUT_DRIVER', 'collection'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Index Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify a prefix that will be applied to all search index
|
||||
| names used by Scout. This prefix may be useful if you have multiple
|
||||
| "tenants" or applications sharing the same search infrastructure.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('SCOUT_PREFIX', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Data Syncing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to control if the operations that sync your data
|
||||
| with your search engines are queued. When this is set to "true" then
|
||||
| all automatic data syncing will get queued for better performance.
|
||||
|
|
||||
*/
|
||||
|
||||
'queue' => env('SCOUT_QUEUE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Transactions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option determines if your data will only be synced
|
||||
| with your search indexes after every open database transaction has
|
||||
| been committed, thus preventing any discarded data from syncing.
|
||||
|
|
||||
*/
|
||||
|
||||
'after_commit' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Chunk Sizes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options allow you to control the maximum chunk size when you are
|
||||
| mass importing data into the search engine. This allows you to fine
|
||||
| tune each of these chunk sizes based on the power of the servers.
|
||||
|
|
||||
*/
|
||||
|
||||
'chunk' => [
|
||||
'searchable' => 500,
|
||||
'unsearchable' => 500,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Soft Deletes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows to control whether to keep soft deleted records in
|
||||
| the search indexes. Maintaining soft deleted records can be useful
|
||||
| if your application still needs to search for the records later.
|
||||
|
|
||||
*/
|
||||
|
||||
'soft_delete' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Identify User
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to control whether to notify the search engine
|
||||
| of the user performing the search. This is sometimes useful if the
|
||||
| engine supports any analytics based on this application's users.
|
||||
|
|
||||
| Supported engines: "algolia"
|
||||
|
|
||||
*/
|
||||
|
||||
'identify' => env('SCOUT_IDENTIFY', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Algolia Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your Algolia settings. Algolia is a cloud hosted
|
||||
| search engine which works great with Scout out of the box. Just plug
|
||||
| in your application ID and admin API key to get started searching.
|
||||
|
|
||||
*/
|
||||
|
||||
'algolia' => [
|
||||
'id' => env('ALGOLIA_APP_ID', ''),
|
||||
'secret' => env('ALGOLIA_SECRET', ''),
|
||||
'index-settings' => [
|
||||
// 'users' => [
|
||||
// 'searchableAttributes' => ['id', 'name', 'email'],
|
||||
// 'attributesForFaceting'=> ['filterOnly(email)'],
|
||||
// ],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Meilisearch Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your Meilisearch settings. Meilisearch is an open
|
||||
| source search engine with minimal configuration. Below, you can state
|
||||
| the host and key information for your own Meilisearch installation.
|
||||
|
|
||||
| See: https://www.meilisearch.com/docs/learn/configuration/instance_options#all-instance-options
|
||||
|
|
||||
*/
|
||||
|
||||
'meilisearch' => [
|
||||
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
|
||||
'key' => env('MEILISEARCH_KEY'),
|
||||
'index-settings' => [
|
||||
'articles' => [
|
||||
'searchableAttributes' => [
|
||||
'sTitle',
|
||||
'sTitleSub',
|
||||
'sCategoryName',
|
||||
'sContent'
|
||||
],
|
||||
'filterableAttributes' => ['iStatus', 'iCategoryId', 'iIsTop'],
|
||||
'sortableAttributes' => ['updated_at', 'iIsTop'],
|
||||
'displayedAttributes' => ['*'],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Typesense Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your Typesense settings. Typesense is an open
|
||||
| source search engine using minimal configuration. Below, you will
|
||||
| state the host, key, and schema configuration for the instance.
|
||||
|
|
||||
*/
|
||||
|
||||
'typesense' => [
|
||||
'client-settings' => [
|
||||
'api_key' => env('TYPESENSE_API_KEY', 'xyz'),
|
||||
'nodes' => [
|
||||
[
|
||||
'host' => env('TYPESENSE_HOST', 'localhost'),
|
||||
'port' => env('TYPESENSE_PORT', '8108'),
|
||||
'path' => env('TYPESENSE_PATH', ''),
|
||||
'protocol' => env('TYPESENSE_PROTOCOL', 'http'),
|
||||
],
|
||||
],
|
||||
'nearest_node' => [
|
||||
'host' => env('TYPESENSE_HOST', 'localhost'),
|
||||
'port' => env('TYPESENSE_PORT', '8108'),
|
||||
'path' => env('TYPESENSE_PATH', ''),
|
||||
'protocol' => env('TYPESENSE_PROTOCOL', 'http'),
|
||||
],
|
||||
'connection_timeout_seconds' => env('TYPESENSE_CONNECTION_TIMEOUT_SECONDS', 2),
|
||||
'healthcheck_interval_seconds' => env('TYPESENSE_HEALTHCHECK_INTERVAL_SECONDS', 30),
|
||||
'num_retries' => env('TYPESENSE_NUM_RETRIES', 3),
|
||||
'retry_interval_seconds' => env('TYPESENSE_RETRY_INTERVAL_SECONDS', 1),
|
||||
],
|
||||
// 'max_total_results' => env('TYPESENSE_MAX_TOTAL_RESULTS', 1000),
|
||||
'model-settings' => [
|
||||
// User::class => [
|
||||
// 'collection-schema' => [
|
||||
// 'fields' => [
|
||||
// [
|
||||
// 'name' => 'id',
|
||||
// 'type' => 'string',
|
||||
// ],
|
||||
// [
|
||||
// 'name' => 'name',
|
||||
// 'type' => 'string',
|
||||
// ],
|
||||
// [
|
||||
// 'name' => 'created_at',
|
||||
// 'type' => 'int64',
|
||||
// ],
|
||||
// ],
|
||||
// 'default_sorting_field' => 'created_at',
|
||||
// ],
|
||||
// 'search-parameters' => [
|
||||
// 'query_by' => 'name'
|
||||
// ],
|
||||
// ],
|
||||
],
|
||||
'import_action' => env('TYPESENSE_IMPORT_ACTION', 'upsert'),
|
||||
],
|
||||
|
||||
];
|
||||
38
config/services.php
Executable file
38
config/services.php
Executable file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
||||
| location for this type of information, allowing packages to have
|
||||
| a conventional file to locate the various service credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'postmark' => [
|
||||
'key' => env('POSTMARK_API_KEY'),
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'key' => env('RESEND_API_KEY'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'notifications' => [
|
||||
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
217
config/session.php
Executable file
217
config/session.php
Executable file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines the default session driver that is utilized for
|
||||
| incoming requests. Laravel supports a variety of storage options to
|
||||
| persist session data. Database storage is a great default choice.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "memcached",
|
||||
| "redis", "dynamodb", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to expire immediately when the browser is closed then you may
|
||||
| indicate that via the expire_on_close configuration option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => (int) env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it's stored. All encryption is performed
|
||||
| automatically by Laravel and you may use the session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => env('SESSION_ENCRYPT', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the "file" session driver, the session files are placed
|
||||
| on disk. The default storage location is defined here; however, you
|
||||
| are free to provide another location where they should be stored.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => env('SESSION_CONNECTION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table to
|
||||
| be used to store sessions. Of course, a sensible default is defined
|
||||
| for you; however, you're welcome to change this to another table.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => env('SESSION_TABLE', 'sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using one of the framework's cache driven session backends, you may
|
||||
| define the cache store which should be used to store the session data
|
||||
| between requests. This must match one of your defined cache stores.
|
||||
|
|
||||
| Affects: "dynamodb", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => env('SESSION_STORE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the session cookie that is created by
|
||||
| the framework. Typically, you should not need to change this value
|
||||
| since doing so does not grant a meaningful security improvement.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug((string) env('APP_NAME', 'laravel')).'-session'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application, but you're free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('SESSION_PATH', '/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the domain and subdomains the session cookie is
|
||||
| available to. By default, the cookie will be available to the root
|
||||
| domain without subdomains. Typically, this shouldn't be changed.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. It's unlikely you should disable this option.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => env('SESSION_HTTP_ONLY', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Same-Site Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines how your cookies behave when cross-site requests
|
||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||
| will set this value to "lax" to permit secure cross-site requests.
|
||||
|
|
||||
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
||||
|
|
||||
| Supported: "lax", "strict", "none", null
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => env('SESSION_SAME_SITE', 'lax'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Partitioned Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will tie the cookie to the top-level site for
|
||||
| a cross-site context. Partitioned cookies are accepted by the browser
|
||||
| when flagged "secure" and the Same-Site attribute is set to "none".
|
||||
|
|
||||
*/
|
||||
|
||||
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
|
||||
|
||||
];
|
||||
1
database/.gitignore
vendored
Executable file
1
database/.gitignore
vendored
Executable file
@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
44
database/factories/UserFactory.php
Executable file
44
database/factories/UserFactory.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
25
database/seeders/DatabaseSeeder.php
Executable file
25
database/seeders/DatabaseSeeder.php
Executable file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
}
|
||||
}
|
||||
19555
laji/a7edd6b036ef4b8e7cd93f435386d4da.js
Executable file
19555
laji/a7edd6b036ef4b8e7cd93f435386d4da.js
Executable file
File diff suppressed because one or more lines are too long
69
laji/buding_laji.js
Executable file
69
laji/buding_laji.js
Executable file
@ -0,0 +1,69 @@
|
||||
<script data-no-optimize="1">
|
||||
/**
|
||||
* [补丁 1] 环境校验
|
||||
* 处理 Cookie 逻辑,确保主题 JS 不会因为找不到 Vary 而报错
|
||||
*/
|
||||
var litespeed_vary = document.cookie.replace(/(?:(?:^|.*;\s*)_lscache_vary\s*\=\s*([^;]*).*$)|^.*$/,"");
|
||||
if(!litespeed_vary) {
|
||||
fetch("/wp-content/plugins/litespeed-cache/guest.vary.php",{method:"POST",cache:"no-cache",redirect:"follow"})
|
||||
.then(e=>e.json()).then(e=>{
|
||||
if(e.hasOwnProperty("reload") && "yes"==e.reload) window.location.reload(!0);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script data-optimized="1" type="litespeed/javascript" data-src="/wp-content/litespeed/js/a7edd6b036ef4b8e7cd93f435386d4da.js"></script>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* [补丁 2] 侧边栏/动画激活引擎
|
||||
*/
|
||||
const litespeed_ui_events = ["mouseover","click","keydown","wheel","touchmove","touchstart"];
|
||||
var urlCreator = window.URL || window.webkitURL;
|
||||
|
||||
// 交互强制加载
|
||||
function litespeed_load_delayed_js_force() {
|
||||
litespeed_ui_events.forEach(e => {
|
||||
window.removeEventListener(e, litespeed_load_delayed_js_force, {passive:!0});
|
||||
});
|
||||
litespeed_load_delayed_js();
|
||||
}
|
||||
litespeed_ui_events.forEach(e => {
|
||||
window.addEventListener(e, litespeed_load_delayed_js_force, {passive:!0});
|
||||
});
|
||||
|
||||
async function litespeed_load_delayed_js() {
|
||||
let scripts = [];
|
||||
document.querySelectorAll('script[type="litespeed/javascript"]').forEach(e => { scripts.push(e); });
|
||||
for (var i in scripts) {
|
||||
await new Promise(res => litespeed_load_one(scripts[i], res));
|
||||
}
|
||||
// [核心漏点] 必须手动触发这个事件,侧边栏才会初始化
|
||||
document.dispatchEvent(new Event("DOMContentLiteSpeedLoaded"));
|
||||
window.dispatchEvent(new Event("DOMContentLiteSpeedLoaded"));
|
||||
}
|
||||
|
||||
function litespeed_load_one(t, e) {
|
||||
var d = document.createElement("script");
|
||||
d.addEventListener("load", e);
|
||||
d.addEventListener("error", e);
|
||||
t.getAttributeNames().forEach(name => {
|
||||
if (name != "type") d.setAttribute(name == "data-src" ? "src" : name, t.getAttribute(name));
|
||||
});
|
||||
d.type = "text/javascript";
|
||||
if (!d.src && t.textContent) {
|
||||
d.src = litespeed_inline2src(t.textContent);
|
||||
}
|
||||
t.after(d);
|
||||
t.remove();
|
||||
}
|
||||
|
||||
function litespeed_inline2src(t) {
|
||||
try {
|
||||
var d = urlCreator.createObjectURL(new Blob([t.replace(/^(?:)?$/gm, "$1")], {type: "text/javascript"}));
|
||||
} catch (e) {
|
||||
d = "data:text/javascript;base64," + btoa(t.replace(/^(?:)?$/gm, "$1"));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
</script>
|
||||
2533
package-lock.json
generated
Executable file
2533
package-lock.json
generated
Executable file
File diff suppressed because it is too large
Load Diff
25
package.json
Executable file
25
package.json
Executable file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://www.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"axios": "^1.11.0",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^2.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"vite": "^7.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^7.1.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "^5.3.8",
|
||||
"clipboard": "^2.0.11",
|
||||
"infinite-scroll": "^5.0.0",
|
||||
"jquery": "^4.0.0"
|
||||
}
|
||||
}
|
||||
35
phpunit.xml
Executable file
35
phpunit.xml
Executable file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<directory>tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>app</directory>
|
||||
</include>
|
||||
</source>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="BROADCAST_CONNECTION" value="null"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
<env name="NIGHTWATCH_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
25
public/.htaccess
Executable file
25
public/.htaccess
Executable file
@ -0,0 +1,25 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews -Indexes
|
||||
</IfModule>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Handle X-XSRF-Token Header
|
||||
RewriteCond %{HTTP:x-xsrf-token} .
|
||||
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
|
||||
|
||||
# Redirect Trailing Slashes If Not A Folder...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Send Requests To Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
||||
0
public/favicon.ico
Executable file
0
public/favicon.ico
Executable file
42
public/index.php
Executable file
42
public/index.php
Executable file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
function t($xData)
|
||||
{
|
||||
_t($xData);
|
||||
}
|
||||
|
||||
function tt($xData)
|
||||
{
|
||||
_t($xData, 1);
|
||||
}
|
||||
|
||||
function _t($aData, $iType = 0)
|
||||
{
|
||||
if (env("is_loc") == true) {
|
||||
echo "<pre>";
|
||||
var_dump($aData);
|
||||
|
||||
if ($iType == 1) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the request...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->handleRequest(Request::capture());
|
||||
2
public/robots.txt
Executable file
2
public/robots.txt
Executable file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
24
public/sakai/css/common.css
Executable file
24
public/sakai/css/common.css
Executable file
@ -0,0 +1,24 @@
|
||||
.o-thumb-img {
|
||||
width: 1001%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* 效果等同于 background-size: cover */
|
||||
}
|
||||
|
||||
/* 初始态:透明且向下偏移一点点 */
|
||||
.ajax-item-fade-in {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.6s cubic-bezier(0.21, 0.6, 0.35, 1);
|
||||
}
|
||||
|
||||
/* 激活态:自然升起淡入 */
|
||||
.ajax-item-fade-in.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.subCopy {
|
||||
background-color:gray;
|
||||
padding:6px;
|
||||
color:#fff;
|
||||
}
|
||||
6
public/sakai/css/single.css
Executable file
6
public/sakai/css/single.css
Executable file
@ -0,0 +1,6 @@
|
||||
|
||||
.subCopy {
|
||||
background-color:gray;
|
||||
padding:6px;
|
||||
color:#fff;
|
||||
}
|
||||
30
public/sakai/js/articleList.js
Executable file
30
public/sakai/js/articleList.js
Executable file
@ -0,0 +1,30 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$(".btn-more").click(function() {
|
||||
|
||||
$("#pageNo").val(parseInt($("#pageNo").val()) + 1);
|
||||
|
||||
$.ajax({
|
||||
url: '/' + $("#type").val(),
|
||||
type: 'GET',
|
||||
data: {
|
||||
pageNo: $("#pageNo").val(),
|
||||
tagId: $("#tagId").val(),
|
||||
search: $("#search").val(),
|
||||
},
|
||||
success: function(response) {
|
||||
$(".list-home").append(response);
|
||||
|
||||
if ($("#haveMore").val() == 0) {
|
||||
$(".btn-more").hide();
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
66
public/sakai/js/articleSinge.js
Executable file
66
public/sakai/js/articleSinge.js
Executable file
@ -0,0 +1,66 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// 复制
|
||||
var clipboard = new ClipboardJS('.copy-permalink');
|
||||
clipboard.on('success', function(e) {
|
||||
ncPopupTips(!0, '复制成功');
|
||||
e.clearSelection();
|
||||
});
|
||||
clipboard.on('error', function(e) {
|
||||
ncPopupTips(1, '复制失败');
|
||||
});
|
||||
|
||||
// 基本变量
|
||||
$id = $("#article_id").val();
|
||||
|
||||
// 修复加载
|
||||
$(".widget_nice_tag_cloud").load("/widget/toptags.html");
|
||||
|
||||
// 获取点赞数
|
||||
$.ajax({
|
||||
url: '/helper/like/get/'+$id,
|
||||
type: 'GET',
|
||||
data: {},
|
||||
success: function(response) {
|
||||
|
||||
$(".like-count").html(response)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// 获取过去时间
|
||||
$.ajax({
|
||||
url: '/helper/timelost/'+$("#createdAt").val(),
|
||||
type: 'GET',
|
||||
data: {},
|
||||
success: function(response) {
|
||||
|
||||
$("#timeLost").html(response)
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// 点赞动作
|
||||
$("[data-action='like']").click(function(){
|
||||
|
||||
$.ajax({
|
||||
url: '/helper/like/up/'+$id,
|
||||
type: 'GET',
|
||||
data: {},
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.iCode == 1) {
|
||||
$(".like-count").html(response.iLikeNow)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return false;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
14
public/sakai/js/common.js
Executable file
14
public/sakai/js/common.js
Executable file
@ -0,0 +1,14 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
$('.search-input').on('keypress', function(event) {
|
||||
if (event.which === 13) {
|
||||
window.location.href = "/mix?search="+$(this).val();
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
// $(".widget_nice_tag_cloud").load("/widget/toptags.html");
|
||||
|
||||
// $(".site-main").click();
|
||||
|
||||
})
|
||||
BIN
public/sites/vfn/icon.png
Executable file
BIN
public/sites/vfn/icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
public/sites/vfn/siteimg.jpg
Executable file
BIN
public/sites/vfn/siteimg.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
3916
public/static/js/lib/jquery.min.js
vendored
Executable file
3916
public/static/js/lib/jquery.min.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
11125
public/static/js/ui/core-ui.min.js
vendored
Executable file
11125
public/static/js/ui/core-ui.min.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
19555
public/static/js/ui/core-ui.min_yuanshi.js
Executable file
19555
public/static/js/ui/core-ui.min_yuanshi.js
Executable file
File diff suppressed because one or more lines are too long
2
public/upload/images/.gitignore
vendored
Executable file
2
public/upload/images/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user