[
  {
    "path": ".dockerignore",
    "content": "**/.classpath\n**/.dockerignore\n**/.env\n**/.git\n**/.gitignore\n**/.project\n**/.settings\n**/.toolstarget\n**/.vs\n**/.vscode\n**/*.*proj.user\n**/*.dbmdl\n**/*.jfm\n**/azds.yaml\n**/bin\n**/charts\n**/docker-compose*\n**/Dockerfile*\n**/node_modules\n**/npm-debug.log\n**/obj\n**/secrets.dev.yaml\n**/values.dev.yaml\nLICENSE\nREADME.md"
  },
  {
    "path": ".github/workflows/docker-image-release.yml",
    "content": "name: Docker Image release\n\non:\n  release:\n    types: [published]\n\njobs:\n\n  build:\n\n    runs-on: ubuntu-latest\n    \n    steps: \n    \n    - name: Check out the repo\n      uses: actions/checkout@v2\n      \n    - name: Login to DockerHub\n      uses: docker/login-action@v1\n      with:\n        username: ${{ secrets.DOCKERHUB_USERNAME }}\n        password: ${{ secrets.DOCKERHUB_TOKEN }}\n        \n    - name: Docker Build & Push to Docker Hub \n      uses: docker/build-push-action@v2\n      with:\n        context: .\n        file: ./yopngs/Dockerfile\n        platforms: linux/amd64\n        push: true\n        tags: xpnas/yopngs:latest\n\n    - name: 'Report Suecss'\n      run: curl ${{ secrets.INOTIFY }}/yopngs/latestIsOk!\n"
  },
  {
    "path": ".github/workflows/docker-image.yml",
    "content": "name: Docker Image master\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  build:\n\n    runs-on: ubuntu-latest\n    \n    steps: \n    \n    - name: Check out the repo\n      uses: actions/checkout@v2\n      \n    - name: Login to DockerHub\n      uses: docker/login-action@v1\n      with:\n        username: ${{ secrets.DOCKERHUB_USERNAME }}\n        password: ${{ secrets.DOCKERHUB_TOKEN }}\n        \n    - name: Docker Build & Push to Docker Hub \n      uses: docker/build-push-action@v2\n      with:\n        context: .\n        file: ./yopngs/Dockerfile\n        platforms: linux/amd64\n        push: true\n        tags: xpnas/yopngs:master\n\n    - name: 'Report Suecss'\n      run: curl ${{ secrets.INOTIFY }}/yopngs/masterIsOk!\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n/iimages/iimages/appsettings.json\n/iimages/iimages/wwwroot/.vscode\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 xpnas\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# yopngs\n\n示例站点：[有图床](https://yopngs.com)\n\n[![Docker Image master](https://github.com/xpnas/yopngs/actions/workflows/docker-image.yml/badge.svg?branch=master)](https://github.com/xpnas/yopngs/actions/workflows/docker-image.yml)\n\n一个纯粹的开源图床，聚焦图床核心功能，抛去用户验证、上传限制，自带鉴黄功能\n\n支持鉴黄、支持压缩、支持本地存储、COS存储、OSS存储、B2存储\n\n## 使用方法\n\n### 发布版\n\n  请先确认已安装DockerCompose\n  ```\n  wget \"https://raw.githubusercontent.com/xpnas/yopngs/master/docker-compose.yml\" \n  ```\n  ```\n  docker-compose up -d\n   ```\n\n### 配置Nginx代理\n  ``` yml\n  server\n  {\n   location / {\n   proxy_pass http://localhost:8081;\n   proxy_http_version 1.1;\n   proxy_set_header Upgrade $http_upgrade;\n   proxy_set_header Connection keep-alive;\n   proxy_set_header Host $host;\n   proxy_cache_bypass $http_upgrade;\n    }\n  }\n  ```\n\n## 配置存储源\n\n所有配置都在config目录下的setting.json文件,可参照defaultsetting.json修改\n\n### 本地存储\nDISKStores节点，支持多个，可使用docker启动命令映射Rclone挂载的磁盘\n``` json\n  \"DISKStores\": [\n    {\n      \"diskfloder\": \"/yopngs\",//本地目录，docker请做映射\n      \"webfloder\": \"/v1\",//url目录，如https://yopngs.com/v1/2022/01/01/xxxxx.png\n      \"name\": \"yopngs\",//主界面下拉显示名称，随意填写\n      \"type\": \"yopngs\",//内部类型，随意填写\n      \"index\": 0,//主界面下拉排序，越小越优先\n      \"active\": true//是否激活\n    },\n```\n### Backblaze2存储\nB2Stores节点，支持多个\n```json\n\"B2Stores\": [\n  {\n    \"KeyId\": \"xx\",\n    \"ApplicationKey\": \"xx\",\n    \"BucketId\": \"xx\",\n    \"Domain\": \"https://xx.com\",//建议在B2前套上Cloudflare，使用自定义域名\n    \"Safe\":false,//建议使用Cloudflare规则以避免暴露B2信息，防止有心人刷B2流量，开启后将去除Url中的file/BucketName\n    \"name\": \"backblazeb2\",\n    \"type\": \"backblazeb2\",\n    \"index\": \"2\",\n    \"active\": true\n   }\n  ```\n### 腾讯COS存储\nCOSStores节点，支持多个\n``` json\n  \"COSStores\": [\n    {\n      \"region\": \"ap-shanghai\",\n      \"bucket\": \"xx\",\n      \"SECRET_ID\": \"xx\",\n      \"SECRET_KEY\": \"xx\",\n      \"Domain\": \"https://xx.com\",\n      \"name\": \"COS\",\n      \"type\": \"COS\",\n      \"index\": 1,\n      \"active\": false\n    }\n  ],\n  ```\n### 阿里OSS存储\nOSSStores节点，支持多个\n``` json\n  \"OSSStores\": [\n    {\n      \"AccessKeyId\": \"xxx\",\n      \"AccessKeySecret\": \"xx\",\n      \"Endpoint\": \"xx\",\n      \"Domain\": \"https://xx.com\",\n      \"name\": \"OSS\",\n      \"type\": \"OSS\",\n      \"index\": \"2\",\n      \"active\": false\n    }\n  ],\n```\n## 其他设置\n\n```json\n  \"GLOBAL\": {\n    \"SIZELIMIT\": 30,//图片大小\n    \"EXTLIMIT\": \".PNG.GIF.JPG.JPEG.BMP\",//类型限制\n    \"NSFW\": true,//鉴黄开关\n    \"NSFWCORE\": 0.5,//鉴黄分数0~1\n    \"NSFWHOST\": \"http://nsfwapi:5000\",//请勿修改\n    \"SERVERHOST\": \"http://yopngs:80\",//请勿修改\n    \"COMPRESS\": false,//是否启用压缩\n    \"COUNT\": 0,\n    \"STARTDATE\": \"2020.01.01\"\n  },\n```\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '2'\n\nservices:\n  yopngs:\n    container_name: yopngs\n    restart: always\n    image: xpnas/yopngs:latest\n    volumes:\n      - /yopngs:/yopngs\n      - /yopngs_config:/app/config\n    ports:\n      - \"8081:80\"\n    networks:\n      - yopngs\n\n  nsfwapi:\n    container_name: nsfwapi\n    restart: always\n    image: eugencepoi/nsfw_api:latest\n    environment:\n      PORT: 5000\n    links:\n      - yopngs\n    ports:\n      - \"8082:5000\"\n    networks:\n      - yopngs\n\nnetworks:\n  yopngs:\n    driver: bridge\n"
  },
  {
    "path": "yopngs/Controllers/ApiController.cs",
    "content": "﻿using Iimages.IStore;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.Hosting;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nnamespace Iimages.Controllers\n{\n    [ApiController]\n    [Route(\"[controller]\")]\n    public class ApiController : ControllerBase\n    {\n        private static object CountValue = \"\";\n\n        private static object DateValue = \"\";\n\n        private IHostEnvironment mEenvironment;\n\n\n        public ApiController(IHostEnvironment hostEnvironment)\n        {\n            mEenvironment = hostEnvironment;\n            CountValue = GetCountValue(false);\n        }\n\n        [HttpPost]\n        [Route(\"upload\")]\n        public async Task<IActionResult> UploadAsync(string type)\n        {\n            return await Task.Run(() =>\n              {\n                  //类型检测\n                  IDataStore store;\n                  if (!SotreCenter.Stores.TryGetValue(type, out store))\n                  {\n                      return Ok(new\n                      {\n                          UploadNode = type,\n                          code = 404,\n                          data = new\n                          {\n                              msg = \"ERROR\",\n                              name = \"ERROR\",\n                              url = \"ERROR\",\n                          },\n                          msg = string.Format(\"上传失败!不支持的类型{0}!\", type)\n                      });\n                  }\n\n                  //文件数量检测\n                  var form = Request.Form;\n                  var files = form.Files;\n                  if (files.Count != 1)\n                  {\n                      return Ok(new\n                      {\n                          UploadNode = type,\n                          code = 404,\n                          data = new\n                          {\n                              msg = \"ERROR\",\n                              name = \"ERROR\",\n                              url = \"ERROR\",\n                          },\n                          msg = \"上传失败!\"\n                      });\n                  }\n\n                  //文件大小检测\n                  var formFile = files[0];\n                  var sizeLimit = Convert.ToInt32(SotreCenter.Config[\"GLOBAL:SIZELIMIT\"]);\n                  if (formFile.Length < 0 || formFile.Length > sizeLimit * 1024 * 1024)\n                  {\n                      return Ok(new\n                      {\n                          UploadNode = type,\n                          code = 404,\n                          data = new\n                          {\n                              msg = \"ERROR\",\n                              name = \"ERROR\",\n                              url = \"ERROR\",\n                          },\n                          msg = string.Format(\"上传失败!限制大小{0}-{1}M\", 0, sizeLimit)\n                      });\n                  }\n\n                  //格式检测\n                  var sourceName = formFile.FileName;\n                  var fileExt = Path.GetExtension(sourceName);\n                  var extLimit = SotreCenter.Config[\"GLOBAL:EXTLIMIT\"];\n                  if (!extLimit.Contains(fileExt.ToUpper()))\n                  {\n                      return Ok(new\n                      {\n                          UploadNode = type,\n                          code = 404,\n                          data = new\n                          {\n                              msg = \"ERROR\",\n                              name = \"ERROR\",\n                              url = \"ERROR\",\n                          },\n                          msg = string.Format(\"上传失败!限制格式{0}\", 0, extLimit)\n                      });\n                  }\n\n                  try\n                  {\n\n                      //黄图检测\n                      var localName = Guid.NewGuid().ToString() + fileExt;\n                      var webServer = Request.Scheme + \"://\" + Request.Host;\n                      var nswFilePath = string.Empty;\n\n\n                      using (var memoery = new MemoryStream())\n                      {\n                          memoery.SetLength(formFile.Length);\n                          formFile.CopyTo(memoery);\n                          var maps = memoery.GetBuffer();\n\n                          //压缩\n                          if (SotreCenter.COMPRESS && (fileExt.ToUpper() == \".PNG\" || fileExt.ToUpper() == \".JPG\"))\n                          {\n                              maps = SotreCenter.StoreCompress.Compress(maps);\n                          }\n\n\n                          //鉴黄\n                          if (SotreCenter.NSFW && !SotreCenter.NSFWCHECK.PassSex(maps))\n                          {\n                              return Ok(new\n                              {\n                                  UploadNode = type,\n                                  code = 404,\n                                  data = new\n                                  {\n                                      msg = \"ERROR\",\n                                      name = sourceName,\n                                      url = \"ERROR\",\n                                  },\n                                  msg = \"小撸怡情，大撸伤身!\"\n                              });\n                          }\n\n\n                          //存储\n                          var cdnUrl = string.Empty;\n                          if (store.Up(maps, localName, webServer, ref cdnUrl))\n                          {\n                              CountValue = GetCountValue(true);\n\n                              return Ok(new\n                              {\n                                  UploadNode = type,\n                                  code = 200,\n                                  data = new\n                                  {\n                                      msg = \"OK\",\n                                      name = sourceName,\n                                      url = cdnUrl,\n                                  },\n                                  msg = \"上传成功!\"\n                              });\n                          }\n\n                          return Ok(new\n                          {\n                              UploadNode = type,\n                              code = 502,\n                              data = new\n                              {\n                                  msg = \"ERROR\",\n                                  name = sourceName,\n                                  url = \"ERROR\",\n                              },\n                              msg = \"上传失败!\"\n                          });\n                      }\n                  }\n                  catch\n                  {\n                      return Ok(new\n                      {\n                          UploadNode = type,\n                          code = 502,\n                          data = new\n                          {\n                              msg = \"ERROR\",\n                              name = sourceName,\n                              url = \"ERROR\",\n                          },\n                          msg = \"上传失败!\"\n                      });\n\n                  }\n              });\n        }\n\n        [HttpGet]\n        [Route(\"supports\")]\n        public List<SupportType> GetSupports()\n        {\n            return SotreCenter.Supports;\n        }\n\n        [HttpGet]\n        [Route(\"info\")]\n        public string Info()\n        {\n            var startDate = DateTime.ParseExact(GetDateValue() as string, \"yyyyMMdd\", System.Globalization.CultureInfo.CurrentCulture);\n            var timeSpan = DateTime.Now - startDate;\n            return string.Format(\"本站已运行 {0} 天,托管 {1} 张图片\", timeSpan.Days, Convert.ToInt64(CountValue));\n\n        }\n\n\n        [HttpGet]\n        public string Get()\n        {\n            return \"It's Work\";\n        }\n\n        private object GetCountValue(bool add)\n        {\n            lock (CountValue)\n            {\n                var filePath = Path.Combine(mEenvironment.ContentRootPath, \"config/count.txt\");\n                if (!System.IO.File.Exists(filePath))\n                    System.IO.File.WriteAllText(filePath, \"0\");\n\n                CountValue = System.IO.File.ReadAllText(filePath);\n\n                if (add)\n                {\n                    CountValue = (Convert.ToInt64(CountValue) + 1).ToString();\n                    System.IO.File.WriteAllText(filePath, CountValue as string);\n                }\n            }\n\n            return CountValue;\n        }\n\n        private object GetDateValue()\n        {\n\n            lock (DateValue)\n            {\n                if (string.IsNullOrEmpty(DateValue as string))\n                {\n                    var filePath = Path.Combine(mEenvironment.ContentRootPath, \"config/date.txt\");\n                    if (!System.IO.File.Exists(filePath))\n                        System.IO.File.WriteAllText(filePath, DateTime.Now.ToString(\"yyyyMMdd\"));\n\n                    DateValue = System.IO.File.ReadAllText(filePath);\n                }\n            }\n\n            return DateValue;\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Dockerfile",
    "content": "#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.\n\nFROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base\nWORKDIR /app\nEXPOSE 80\nEXPOSE 443\n\nFROM mcr.microsoft.com/dotnet/sdk:3.1 AS build\nWORKDIR /src\nCOPY [\"yopngs/yopngs.csproj\", \"yopngs/\"]\nRUN dotnet restore \"yopngs/yopngs.csproj\"\nCOPY . .\nWORKDIR \"/src/yopngs\"\nRUN dotnet build \"yopngs.csproj\" -c Release -o /app/build\n\nFROM build AS publish\nRUN dotnet publish \"yopngs.csproj\" -c Release -o /app/publish\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"yopngs.dll\"]"
  },
  {
    "path": "yopngs/IStore/IDataStore.cs",
    "content": "﻿using Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\n\nnamespace Iimages.IStore\n{\n    public class SupportType\n    {\n\n        public string Type { get; set; }\n\n        public string Name { get; set; }\n\n        public int Index { get; set; }\n\n        public SupportType(IDataStore store)\n        {\n\n            Name = store.Name;\n            Type = store.Type;\n            Index = store.Index;\n        }\n    }\n\n\n    public interface IDataStore\n    {\n        int Index { get; set; }\n\n        string Name { get; set; }\n\n        string Type { get; set; }\n\n        bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl);\n\n    }\n\n    public interface IStoreFactory\n    {\n        IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration);\n\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/IStoreCheck.cs",
    "content": "﻿namespace Iimages.IStore\n{\n    public interface IStoreCheck\n    {\n\n        bool PassSex(byte[] formFile);\n\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/IStoreCompress.cs",
    "content": "﻿namespace Iimages.IStore\n{\n    public interface IStoreCompress\n    {\n        byte[] Compress(byte[] maps);\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/SotreCenter.cs",
    "content": "﻿using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Reflection;\n\nnamespace Iimages.IStore\n{\n    public static class SotreCenter\n    {\n\n        static SotreCenter()\n        {\n            StoreFactories = Assembly.GetExecutingAssembly()\n                .GetTypes()\n                .Where(e => e.GetInterfaces().Contains(typeof(IStoreFactory)))\n                .Select(e => Activator.CreateInstance(e))\n                .OfType<IStoreFactory>()\n                .ToList();\n        }\n\n        public static void Initialize(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration, IHttpClientFactory httpClientFactory)\n        {\n            var stores = StoreFactories.SelectMany(e => e.GetStores(app, configuration)).ToList();\n            Config = configuration;\n            Stores = stores.ToDictionary(e => e.Type, e => e);\n            Supports = stores.Select(e => new SupportType(e)).OrderBy(e => e.Index).ToList();\n            NSFW = configuration.GetSection(\"GLOBAL\").GetValue<bool>(\"NSFW\");\n            COMPRESS = configuration.GetSection(\"GLOBAL\").GetValue<bool>(\"COMPRESS\");\n            NSFWHOST = configuration.GetSection(\"GLOBAL\").GetValue<string>(\"NSFWHOST\");\n            if (NSFW)\n            {\n                NSFWCHECK = new StoreCheckNsfwWarperResetAPI(app, env, configuration, httpClientFactory);\n            }\n            if (COMPRESS)\n            {\n                StoreCompress = new StoreCompressTinyWarper(app, env, configuration, httpClientFactory);\n            }\n        }\n\n        public static IConfiguration Config { get; private set; }\n\n        public static bool NSFW { get; private set; }\n\n        public static string NSFWHOST { get; private set; }\n\n        public static bool COMPRESS { get; private set; }\n\n        public static List<IStoreFactory> StoreFactories { get; private set; }\n\n        public static Dictionary<string, IDataStore> Stores { get; private set; }\n\n        public static List<SupportType> Supports { get; private set; }\n\n        public static IStoreCheck NSFWCHECK { get; private set; }\n\n        public static IStoreCompress StoreCompress { get; private set; }\n\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/StoreBase.cs",
    "content": "﻿namespace Iimages.IStore\n{\n    public abstract class StoreBase : IDataStore\n    {\n        public int Index { get; set; }\n\n        public string Name { get; set; }\n\n        public string Type { get; set; }\n\n        public abstract bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl);\n\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/StoreCheckNsfwWarperResetAPI.cs",
    "content": "﻿using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.FileProviders;\nusing Newtonsoft.Json.Linq;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Runtime.InteropServices;\nusing System.Threading.Tasks;\n\nnamespace Iimages.IStore\n{\n    public class StoreCheckNsfwWarperResetAPI : IStoreCheck\n    {\n        private readonly string NSFWFLODER;\n        private readonly IHttpClientFactory HttpClientFactory;\n        private readonly string SERVERHOST;\n        private readonly string NSFWHOST;\n        private readonly double NSFWCORE;\n\n        public StoreCheckNsfwWarperResetAPI(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration config, IHttpClientFactory httpClientFactory)\n        {\n            HttpClientFactory = httpClientFactory;\n            if (config.GetSection(\"GLOBAL\") != null)\n            {\n                NSFWCORE = Convert.ToDouble(config[\"GLOBAL:NSFWCORE\"]);\n                NSFWHOST = Convert.ToString(config[\"GLOBAL:NSFWHOST\"]);\n                SERVERHOST = Convert.ToString(config[\"GLOBAL:SERVERHOST\"]);\n\n                NSFWFLODER = Path.Combine(env.ContentRootPath, \"nsfw\");\n                if (!Directory.Exists(NSFWFLODER))\n                {\n                    Directory.CreateDirectory(NSFWFLODER);\n                }\n\n                app.UseStaticFiles(new StaticFileOptions\n                {\n                    FileProvider = new PhysicalFileProvider(NSFWFLODER),\n                    RequestPath = \"/nsfw\"\n                });\n            }\n        }\n\n        public bool PassSex(byte[] formFile)\n        {\n\n            //保存到鉴黄目录\n            var localName = Guid.NewGuid().ToString() + \".png\";\n            var nsfwFile = string.Format(\"{0}/{1}\", NSFWFLODER, localName);\n            File.WriteAllBytes(nsfwFile, formFile);\n            try\n            {\n                var nsfwFileUrl = string.Format(\"{0}/{1}/{2}\", SERVERHOST, \"nsfw\", localName);\n                var url = string.Format(\"{0}{1}\", NSFWHOST, \"/?url=\" + nsfwFileUrl);\n                var request = new HttpRequestMessage(HttpMethod.Get, url);\n                var response = HttpClientFactory.CreateClient().SendAsync(request).Result;\n                var result = response.Content.ReadAsStringAsync().Result;\n                var jsonDoc = System.Text.Json.JsonDocument.Parse(result);\n                var jsonScore = jsonDoc.RootElement.GetProperty(\"score\");\n                if (jsonScore.GetDouble() < NSFWCORE)\n                    return true;\n                return false;\n\n            }\n            catch\n            {\n                return false;\n            }\n            finally\n            {\n                File.Delete(nsfwFile);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/StoreCompressPngQuantWarper.cs",
    "content": "﻿using Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Minimage;\n\nnamespace Iimages.IStore\n{\n    public class StoreCompressPngQuantWarper : IStoreCompress\n    {\n\n        private static PngQuant g_PngQuant;\n\n\n        static StoreCompressPngQuantWarper()\n        {\n            var options = new PngQuantOptions()\n            {\n                QualityMinMax = (65, 80),\n                IEBug = false,\n                Bit = 256\n            };\n            g_PngQuant = new PngQuant(options);\n\n        }\n\n\n        public StoreCompressPngQuantWarper(IApplicationBuilder app, IConfiguration config)\n        {\n\n        }\n\n        public byte[] Compress(byte[] maps)\n        {\n            try\n            {\n                return g_PngQuant.Compress(maps).Result;\n            }\n            catch\n            {\n                return maps;\n            }\n           \n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/IStore/StoreCompressTinyWarper.cs",
    "content": "﻿using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing Minimage;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.RegularExpressions;\n\nnamespace Iimages.IStore\n{\n    public class StoreCompressTinyWarper : IStoreCompress\n    {\n\n        private readonly IHttpClientFactory HttpClientFactory;\n\n\n\n        public StoreCompressTinyWarper(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration config, IHttpClientFactory httpClientFactory)\n        {\n\n            HttpClientFactory = httpClientFactory;\n\n        }\n\n        public byte[] Compress(byte[] maps)\n        {\n            try\n            {\n                var randomIP = string.Format(\"{0}.{1},{2},{3}\", new Random().Next(1, 254), new Random().Next(1, 254), new Random().Next(1, 254), new Random().Next(1, 254));\n                var webRequest = WebRequest.Create(new Uri(\"https://tinypng.com/backend/opt/shrink\"));\n                webRequest.Method = \"Post\";\n                webRequest.ContentType = \"application/x-www-form-urlencoded\";\n                webRequest.Headers.Add(\"rejectUnauthorized\", \"false\");\n                webRequest.Headers.Add(\"Postman-Token\", DateTime.Now.ToString());\n                webRequest.Headers.Add(\"Cache-Control\", \"no-cache\");\n                webRequest.Headers.Add(\"User-Agent\", \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\");\n                webRequest.Headers.Add(\"X-Forwarded-For\", randomIP);\n\n                using (var postStream = webRequest.GetRequestStream())\n                {\n                    var requestStream = webRequest.GetRequestStream();\n                    requestStream.Write(maps, 0, maps.Length);\n                }\n\n                var response = webRequest.GetResponse();\n                using (Stream stream = response.GetResponseStream())\n                {\n                    using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))\n                    {\n                        string resuleJson = reader.ReadToEnd();\n                        var compressUrl = JsonDocument.Parse(resuleJson).RootElement.GetProperty(\"output\").GetProperty(\"url\").GetString();\n\n                        using (WebClient client = new WebClient())\n                        {\n\n                            using (Stream compressStream = client.OpenRead(compressUrl))\n                            {\n                                int readCount = 0;\n                                int bufferSize = 1 << 17;\n\n                                var buffer = new byte[bufferSize];\n                                using (var memory = new MemoryStream())\n                                {\n                                    while ((readCount = compressStream.Read(buffer, 0, bufferSize)) > 0)\n                                    {\n                                        memory.Write(buffer, 0, readCount);\n                                    }\n\n                                    return memory.ToArray();\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            catch(Exception ex)\n            {\n                return maps;\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Program.cs",
    "content": "using Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Hosting;\n\nnamespace Iimages\n{\n    public class Program\n    {\n        public static void Main(string[] args)\n        {\n            CreateHostBuilder(args).Build().Run();\n        }\n\n        public static IHostBuilder CreateHostBuilder(string[] args)\n        {\n            return Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>\n                 {\n                     webBuilder.UseStartup<Startup>();\n                 });\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Properties/launchSettings.json",
    "content": "{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:64028\",\n      \"sslPort\": 44396\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"profiles\": {\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"api\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}/api\",\n      \"publishAllPorts\": true,\n      \"useSSL\": true\n    },\n    \"yopngs\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"index.html\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"applicationUrl\": \"https://localhost:5001;http://localhost5000\"\n    }\n  }\n}"
  },
  {
    "path": "yopngs/Startup.cs",
    "content": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing System.IO;\nusing System.Net.Http;\n\nnamespace Iimages\n{\n    public class Startup\n    {\n        public Startup(IConfiguration configuration)\n        {\n            Configuration = configuration;\n        }\n\n        public IConfiguration Configuration { get; }\n\n        // This method gets called by the runtime. Use this method to add services to the container.\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddControllers();\n            services.AddHttpClient();\n        }\n\n        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\n        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpClientFactory httpClientFactory)\n        {\n            app.UseCors(options =>\n            {\n                options.AllowAnyHeader();\n                options.AllowAnyMethod();\n                options.AllowAnyOrigin();\n            });\n\n            if (env.IsDevelopment())\n            {\n                app.UseDeveloperExceptionPage();\n            }\n\n            app.UseRouting();\n            app.UseAuthorization();\n            app.UseStaticFiles();\n            app.UseFileServer();\n\n            if (!Directory.Exists(Path.Combine(env.ContentRootPath, \"config\")))\n                Directory.CreateDirectory(Path.Combine(env.ContentRootPath, \"config\"));\n\n            var filePath = Path.Combine(env.ContentRootPath, \"config/setting.json\");\n            if (!File.Exists(filePath))\n                File.Copy(Path.Combine(env.ContentRootPath, \"defaultsetting.json\"), filePath);\n\n            var config = new ConfigurationBuilder()\n                  .SetBasePath(Directory.GetCurrentDirectory())\n                  .AddJsonFile(\"config/setting.json\", optional: true, reloadOnChange: true)\n                  .Build();\n\n            IStore.SotreCenter.Initialize(app, env,config, httpClientFactory);\n\n            app.UseEndpoints(endpoints =>\n            {\n                endpoints.MapControllers();\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Stores/B2Store.cs",
    "content": "﻿using B2Net;\nusing B2Net.Models;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Iimages.Stores\n{\n    public class B2Store : StoreBase\n    {\n\n        private bool mPersistBucket, mSafe;\n        private string mKeyId, mApplicationKey, mBucketId, mDomain;\n \n\n\n        public B2Store(string keyId, string applicationKey, string bucketId, bool prsistBucket, string domain,bool safe)\n        {\n            mKeyId = keyId;\n            mApplicationKey = applicationKey;\n            mBucketId = bucketId;\n            mPersistBucket = prsistBucket;\n            mDomain = domain;\n            mSafe = safe;\n        }\n\n        public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)\n        {\n            try\n            {\n                var mOptions = new B2Options()\n                {\n                    KeyId = mKeyId,\n                    ApplicationKey = mApplicationKey,\n                    BucketId = mBucketId,\n                    PersistBucket = mPersistBucket\n                };\n                var mClient = new B2Client(mOptions);\n                var result = mClient.Authorize().Result;\n                if (result.Authenticated)\n                {\n                    var timeTag = DateTime.Now.ToString(\"yyyy/MM/dd/\");\n                    var uploadUrl = mClient.Files.GetUploadUrl(mBucketId).Result;\n                    var fileInfo = mClient.Files.Upload(maps, timeTag + localName, uploadUrl).Result;\n                    var fileId = fileInfo.FileId;\n                    if (mSafe)\n                    {\n                        cdnUrl = string.Format(\"{0}/{1}\", mDomain, timeTag + localName);\n                      \n                    }\n                    else\n                    {\n                        cdnUrl = string.Format(\"{0}/file/{1}/{2}\", mDomain, result.Capabilities.BucketName, timeTag + localName);\n                    }\n\n                    return true;\n                }\n                return false;\n            }\n            catch\n            {\n                return false;\n            }\n        }\n    }\n\n\n    public class B2StoreFactory : IStoreFactory\n    {\n        public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)\n        {\n            var results = new List<IDataStore>();\n            var selection = configuration.GetSection(\"B2Stores\");\n            foreach (IConfigurationSection section in selection.GetChildren())\n            {\n                var active = section.GetValue<bool>(\"active\");\n                if (active)\n                {\n                    var keyId = section.GetValue<string>(\"KeyId\");\n                    var applicationKey = section.GetValue<string>(\"ApplicationKey\");\n                    var bucketId = section.GetValue<string>(\"BucketId\");\n                    var persistBucket = section.GetValue<bool>(\"PersistBucket\");\n                    var domain = section.GetValue<string>(\"Domain\");\n                    var safe = section.GetValue<bool>(\"Safe\");\n\n                    var index = section.GetValue<int>(\"index\");\n                    var name = section.GetValue<string>(\"name\");\n                    var type = section.GetValue<string>(\"type\");\n                    var store = new B2Store(keyId, applicationKey, bucketId, persistBucket, domain, safe)\n                    {\n                        Name = name,\n                        Type = type,\n                        Index = index\n                    };\n\n                    results.Add(store);\n                }\n            }\n\n            return results.ToArray();\n\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Stores/COSStore.cs",
    "content": "﻿using COSXML;\nusing COSXML.Auth;\nusing COSXML.Transfer;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Iimages.Stores\n{\n    public class COSStore : StoreBase\n    {\n\n        private CosXml mCosXml;\n\n        private string mBucket, mDomain;\n\n        public COSStore(string bucket, string region, string secretId, string secretKey,string domain)\n        {\n            long durationSecond = 600;\n            var config = new CosXmlConfig.Builder().IsHttps(true).SetRegion(region).SetDebugLog(true).Build();\n            QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider(secretId, secretKey, durationSecond);\n            mCosXml = new CosXmlServer(config, cosCredentialProvider);\n            mBucket = bucket;\n            mDomain = domain;\n        }\n\n        public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)\n        {\n            try\n            { \n                var cosPath = DateTime.Now.ToString(\"/yyyy/MM/dd/\") + localName;\n                var putObjectRequest = new COSXML.Model.Object.PutObjectRequest(mBucket, cosPath, maps);\n                var uploadTask = new COSXMLUploadTask(new COSXML.Model.Object.PutObjectRequest(mBucket, cosPath, maps));\n                COSXML.Model.Object.PutObjectResult result = mCosXml.PutObject(putObjectRequest);\n            \n                if (result.IsSuccessful())\n                {\n                    cdnUrl = string.Format(\"{0}{1}\", mDomain, cosPath);\n                    return true;\n                }\n               \n            }\n            catch (Exception e)\n            {\n                Console.WriteLine(\"CosException: \" + e);\n            }\n\n            return false;\n        }\n    }\n\n    public class CosStoreFactory : IStoreFactory\n    {\n        public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)\n        {\n            var results = new List<IDataStore>();\n            var selection = configuration.GetSection(\"COSStores\");\n            foreach (IConfigurationSection section in selection.GetChildren())\n            {\n                var active = section.GetValue<bool>(\"active\");\n                if (active)\n                {\n                    var region = section.GetValue<string>(\"region\");\n                    var bucket = section.GetValue<string>(\"bucket\");\n\n                    var secretId = section.GetValue<string>(\"SECRET_ID\");\n                    var secretKey = section.GetValue<string>(\"SECRET_KEY\");\n\n                    var domain = section.GetValue<string>(\"Domain\");\n\n                    var index = section.GetValue<int>(\"index\");\n                    var name = section.GetValue<string>(\"name\");\n                    var type = section.GetValue<string>(\"type\");\n\n\n                    var store = new COSStore(bucket, region, secretId, secretKey, domain)\n                    {\n                        Name = name,\n                        Type = type,\n                        Index = index\n                    };\n\n                    results.Add(store);\n                }\n            }\n\n            return results.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Stores/DISKStore.cs",
    "content": "﻿using Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.FileProviders;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\n\nnamespace Iimages.Stores\n{\n\n    public class DISKStore : StoreBase\n    {\n\n        private string m_diskFloder, m_webFloder, m_host;\n\n        public DISKStore(string diskFloder, string webFloder, string host)\n        {\n            m_diskFloder = diskFloder;\n            m_webFloder = webFloder;\n            m_host = host;\n        }\n\n\n        public override bool Up(byte[] maps, string localName, string webServer, ref string cdnUrl)\n        {\n\n            if (!Directory.Exists(m_diskFloder))\n                Directory.CreateDirectory(m_diskFloder);\n\n            var timeTag = DateTime.Now.ToString(\"/yyyy/MM/dd/\");\n            var filePath = m_diskFloder + timeTag + localName;\n            var fileDir = Path.GetDirectoryName(filePath);\n\n            if (!Directory.Exists(fileDir))\n                Directory.CreateDirectory(fileDir);\n\n            File.WriteAllBytes(filePath,maps);\n\n            cdnUrl = string.Format(\"{0}{1}{2}{3}\", string.IsNullOrEmpty(m_host) ? webServer : m_host, m_webFloder, timeTag, localName);\n\n            return true;\n        }\n    }\n\n\n    public class DiskStoreFactory : IStoreFactory\n    {\n        public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)\n        {\n            var results = new List<IDataStore>();\n            var selection = configuration.GetSection(\"DISKStores\");\n            foreach (IConfigurationSection section in selection.GetChildren())\n            {\n                var diskfloder = section.GetValue<string>(\"diskfloder\");\n                var webfloder = section.GetValue<string>(\"webfloder\");\n                var host= section.GetValue<string>(\"host\");\n                if (!Directory.Exists(diskfloder)) \n                    Directory.CreateDirectory(diskfloder);\n                app.UseStaticFiles(new StaticFileOptions\n                {\n                    FileProvider = new PhysicalFileProvider(diskfloder),\n                    RequestPath = webfloder\n                });\n\n                var active = section.GetValue<bool>(\"active\");\n                if (active)\n                {\n                    var name = section.GetValue<string>(\"name\");\n                    var type = section.GetValue<string>(\"type\");\n                    var index = section.GetValue<int>(\"index\");\n\n                    var store = new DISKStore(diskfloder, webfloder,host)\n                    {\n                        Name = name,\n                        Type = type,\n                        Index = index\n                    };\n\n                    results.Add(store);\n                }\n            }\n            return results.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Stores/GithubStore.cs",
    "content": "﻿using Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Octokit;\nusing System.Collections.Generic;\nusing System.IO;\n\nnamespace Iimages.Stores\n{\n    public class GithubStore : StoreBase\n    {\n        public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)\n        {\n            var client = new GitHubClient(new ProductHeaderValue(\"my-cool-app\"));\n            var basicAuth = new Credentials(\"username\", \"password\"); // NOTE: not real credentials\n            client.Credentials = basicAuth;\n\n            using (var archiveContents = File.OpenRead(\"output.zip\"))\n            { // TODO: better sample\n                var assetUpload = new ReleaseAssetUpload()\n                {\n                    FileName = \"my-cool-project-1.0.zip\",\n                    ContentType = \"application/zip\",\n                    RawData = archiveContents\n                };\n                var repository = client.Repository.Release.Get(\"octokit\", \"octokit.net\", 1).Result;\n                var asset = client.Repository.Release.UploadAsset(repository, assetUpload).Result;\n            }\n\n            return true;\n        }\n    }\n    public class GithubFactory : IStoreFactory\n    {\n        public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)\n        {\n            var results = new List<IDataStore>();\n            var selection = configuration.GetSection(\"OSSStores\");\n            foreach (IConfigurationSection section in selection.GetChildren())\n            {\n                //var active = section.GetValue<bool>(\"active\");\n                //if (active)\n                //{\n                //    var acessKeyId = section.GetValue<string>(\"AccessKeyId\");\n                //    var acessKeySecret = section.GetValue<string>(\"AccessKeySecret\");\n                //    var endPoint = section.GetValue<string>(\"Endpoint\");\n                //    var bucket = section.GetValue<string>(\"Bucket\");\n\n                //    var domain = section.GetValue<string>(\"Domain\");\n                //    var index = section.GetValue<int>(\"index\");\n                //    var name = section.GetValue<string>(\"name\");\n                //    var type = section.GetValue<string>(\"type\");\n\n\n                //    var diskStore = new OSSStore(acessKeyId, acessKeySecret, endPoint, bucket, domain)\n                //    {\n                //        Name = name,\n                //        Type = type,\n                //        Index = index\n                //    };\n\n                //    results.Add(diskStore);\n                //}\n            }\n\n            return results.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Stores/OSSStore.cs",
    "content": "﻿using Aliyun.OSS;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\n\nnamespace Iimages.Stores\n{\n    public class OSSStore : StoreBase\n    {\n        private OssClient mOssClient;\n\n        private string mDomain;\n\n\n        public string Bucket { get; private set; }\n\n        public OSSStore(string accessKeyId, string accessKeySecret, string endpoint, string bucket, string domian)\n        {\n            Bucket = bucket;\n            mDomain = domian;\n            mOssClient = new OssClient(endpoint, accessKeyId, accessKeySecret);\n        }\n\n        public override bool Up(byte[] maps,string localName, string localUrl, ref string cdnUrl)\n        {\n            using (var stream = new MemoryStream(maps))\n            {\n                try\n                {\n                    var cosPath = DateTime.Now.ToString(\"yyyy/MM/dd/\") + localName;\n                    mOssClient.PutObject(Bucket, cosPath, stream);\n                    cdnUrl = string.Format(\"{0}/{1}\", mDomain, cosPath);\n                    return true;\n                }\n                catch\n                {\n                    return false;\n                }\n            }\n\n        }\n    }\n\n    public class OSSFactory : IStoreFactory\n    {\n        public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)\n        {\n            var results = new List<IDataStore>();\n            var selection = configuration.GetSection(\"OSSStores\");\n            foreach (IConfigurationSection section in selection.GetChildren())\n            {\n                var active = section.GetValue<bool>(\"active\");\n                if (active)\n                {\n                    var acessKeyId = section.GetValue<string>(\"AccessKeyId\");\n                    var acessKeySecret = section.GetValue<string>(\"AccessKeySecret\");\n                    var endPoint = section.GetValue<string>(\"Endpoint\");\n                    var bucket = section.GetValue<string>(\"Bucket\");\n\n                    var domain = section.GetValue<string>(\"Domain\");\n                    var index = section.GetValue<int>(\"index\");\n                    var name = section.GetValue<string>(\"name\");\n                    var type = section.GetValue<string>(\"type\");\n\n\n                    var store = new OSSStore(acessKeyId, acessKeySecret, endPoint, bucket,domain)\n                    {\n                        Name = name,\n                        Type = type,\n                        Index = index\n                    };\n\n                    results.Add(store);\n                }\n            }\n\n            return results.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/Stores/S3Store.cs",
    "content": "﻿using Amazon.S3;\nusing Amazon.S3.Model;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing System.Collections.Generic;\n\nnamespace Iimages.Stores\n{\n    public class S3Store : StoreBase\n    {\n        AmazonS3Client Client;\n\n        string Domain;\n\n        public S3Store(string secretID, string secretKey, string region, string domain)\n        {\n            Domain = domain;\n  \n            var endPoint = new AmazonS3Config();\n            endPoint.ServiceURL = region;\n          //  endPoint.AuthenticationRegion = region;\n            Client = new AmazonS3Client(secretID, secretKey, endPoint);\n   \n\n        }\n\n        public override bool Up(byte[] maps, string localName,  string localUrl, ref string cdnUrl)\n        {\n           var files= Client.ListBucketsAsync().Result ;\n            var putRequest2 = new PutObjectRequest\n            {\n                BucketName = \"xpnas-assets\",\n                //获取和设置键属性。此键用于标识S3中的对象,上传到s3的路径+文件名，\n                //S3上没有文件夹可以创建一个，参考https://www.cnblogs.com/web424/p/6840207.html\n                Key = localName,\n                //所有者获得完全控制权，匿名主体被授予读访问权。如果\n                //此策略用于对象，它可以从浏览器中读取，无需验证\n                CannedACL = S3CannedACL.PublicRead,\n                //上传的文件路径\n                FilePath = @\"C:\\Users\\Administrator\\Desktop\\favicon.png\",\n                //为对象设置的标记。标记集必须编码为URL查询参数\n                TagSet = new List<Tag>{ new Tag { Key = \"Test\", Value = \"S3Test\"} }\n                //ContentType = \"image/png\"\n            };\n           // putRequest2.Metadata.Add(\"x-amz-meta-title\", \"AwsS3Net\");\n            PutObjectResponse response2 =  Client.PutObjectAsync(putRequest2).Result;\n            return false;\n        }\n\n    }\n\n    public class S3StoreFactory : IStoreFactory\n    {\n        public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)\n        {\n\n            var results = new List<IDataStore>();\n            var selection = configuration.GetSection(\"S3Stores\");\n            foreach (IConfigurationSection section in selection.GetChildren())\n            {\n                var active = section.GetValue<bool>(\"active\");\n                if (active)\n                {\n                    var secretID = section.GetValue<string>(\"secretID\");\n                    var secretKey = section.GetValue<string>(\"secretKey\");\n                    var region = section.GetValue<string>(\"region\");\n                    var domain = section.GetValue<string>(\"Domain\");\n\n                    var index = section.GetValue<int>(\"index\");\n                    var name = section.GetValue<string>(\"name\");\n                    var type = section.GetValue<string>(\"type\");\n\n\n                    var store = new S3Store(secretID, secretKey, region, domain)\n                    {\n                        Name = name,\n                        Type = type,\n                        Index = index\n                    };\n\n                    results.Add(store);\n                }\n            }\n\n            return results.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "yopngs/appsettings.Development.json",
    "content": "{\n  \"AllowedHosts\": \"*\",\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft\": \"Warning\",\n      \"Microsoft.Hosting.Lifetime\": \"Information\"\n    }\n  }\n}"
  },
  {
    "path": "yopngs/appsettings.json",
    "content": "{\n  \"AllowedHosts\": \"*\",\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft\": \"Warning\",\n      \"Microsoft.Hosting.Lifetime\": \"Information\"\n    }\n  }\n}"
  },
  {
    "path": "yopngs/defaultsetting.json",
    "content": "﻿{\n  \"GLOBAL\": {\n    \"SIZELIMIT\": 30,\n    \"EXTLIMIT\": \".PNG.GIF.JPG.JPEG.BMP\",\n    \"NSFW\": true,\n    \"NSFWCORE\": 0.5,\n    \"NSFWHOST\": \"http://nsfwapi:5000\",\n    \"SERVERHOST\": \"http://yopngs:80\",\n    \"COMPRESS\": false,\n    \"COUNT\": 0,\n    \"STARTDATE\": \"2020.01.01\"\n  },\n  \"DISKStores\": [\n    {\n      \"diskfloder\": \"/yopngs\",\n      \"webfloder\": \"/v1\",\n      \"name\": \"yopngs\",\n      \"type\": \"yopngs\",\n      \"index\": 0,\n      \"active\": true\n    }\n  ]\n  //\"B2Stores\": [\n  //  {\n  //    \"KeyId\": \"xx\",\n  //    \"ApplicationKey\": \"xx\",\n  //    \"BucketId\": \"xx\",\n  //    \"Domain\": \"xx\",\n  //    \"Safe\"\": false\n  //    \"name\": \"backblazeb2\",\n  //    \"type\": \"backblazeb2\",\n  //    \"index\": \"2\",\n  //    \"active\": true\n  //  }\n  //\"OSSStores\": [\n  //  {\n  //    \"AccessKeyId\": \"ap-shanghai\",\n  //    \"AccessKeySecret\": \"xx-xx\",\n  //    \"Endpoint\": \"xx\",\n  //    \"Domain\": \"https://xx.com\",\n  //    \"name\": \"OSS\",\n  //    \"type\": \"OSS\",\n  //    \"index\": \"2\",\n  //    \"active\": false\n  //  }\n  //],\n  //\"COSStores\": [\n  //  {\n  //    \"region\": \"ap-shanghai\",\n  //    \"bucket\": \"xx-xx\",\n  //    \"SECRET_ID\": \"xx\",\n  //    \"SECRET_KEY\": \"xx\",\n  //    \"Domain\": \"https://xx.com\",\n  //    \"name\": \"COS\",\n  //    \"type\": \"COS\",\n  //    \"index\": 1,\n  //    \"active\": false\n  //  }\n  //],\n}"
  },
  {
    "path": "yopngs/wwwroot/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-cn\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\"content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, shrink-to-fit=no, viewport-fit=cover\">\n\t<title>有图床</title>\n\t<meta name=\"keywords\" content=\"纯粹图床,免费图床,COS图床,OOS图床\" />\n\t<link rel=\"shortcut icon\" href=\"./favicon.png\" />\n\t<link rel=\"stylesheet\" href=\"./static/style.css\">\n\t<script src=\"./static/jquery.min.js\"></script>\n\t<script src=\"./static/file.js\"></script>\n</head>\n\n<body>\n\t<input id=\"file\" type=\"file\" multiple=\"multiple\" style=\"display: none;\">\n\t<div class=\"container\">\n\t\t<div class=\"upload\">\n\t\t\t<div class=\"title\">\n\t\t\t\t开始上传\n\t\t\t\t<select id=\"type\" value=\"ali\">\n\t\t\t\t</select>\n\t\t\t</div>\n\t\t\t<div class=\"content\" id=\"dragbox\">\n\t\t\t\t<svg class=\"icon\" viewBox=\"0 0 1335 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M1097.060174 392.125217C1073.730783 172.966957 893.261913 0.378435 666.089739 0.378435c-227.127652 0-415.610435 171.920696-430.948174 391.746782C101.910261 415.454609 0 525.356522 0 666.601739c0 149.147826 125.239652 274.476522 274.476522 274.476522h195.828869v-78.669913H274.476522a193.691826 193.691826 0 0 1-195.940174-195.806609c0-102.021565 70.678261-180.580174 172.588522-195.917913l54.561391-8.013913 8.013913-62.553043c16.005565-180.580174 172.588522-321.157565 352.389565-321.157566 180.580174 0 337.029565 141.356522 352.389565 321.157566v62.553043l62.664348 8.013913c101.910261 16.005565 172.477217 93.896348 172.477218 195.917913 0 109.901913-85.904696 195.806609-195.806609 195.806609h-195.917913v78.580869h196.029217c149.147826 0 274.476522-125.261913 274.476522-274.476521 0-141.133913-101.999304-259.072-235.25287-274.387479\"\n\t\t\t\t\t\tp-id=\"2345\" fill=\"#909399\"></path>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M612.218435 364.766609l1.335652 2.003478L389.698783 590.58087l55.229217 55.362782 181.938087-181.938087V1018.88h78.558609v-78.58087h156.471652-156.471652V458.039652l183.808 183.919305 55.340521-55.340522-277.147826-277.058783-55.229217 55.229218z m-141.913044 575.666087h156.471652-156.716521 0.222608z\"\n\t\t\t\t\t\tp-id=\"2346\" fill=\"#909399\"></path>\n\t\t\t\t</svg>\n\t\t\t\t<p class=\"desc\">点击上传 / 粘贴上传 / 拖拽上传</p>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"filelist\">\n\t\t\t<div class=\"title\">\n\t\t\t\t上传列表\n\t\t\t\t<div class=\"copyall\" style=\"display:none\">\n\t\t\t\t\t<button onclick=\"sel(this);\" name=\"xkx\" id=\"_url\">URL</button>\n\t\t\t\t\t<button onclick=\"sel(this);\" name=\"xkx\" id=\"_html\">HTML</button>\n\t\t\t\t\t<button onclick=\"sel(this);\" name=\"xkx\" id=\"_Ubb\">UBB</button>\n\t\t\t\t\t<button onclick=\"sel(this);\" name=\"xkx\" id=\"_markdown\">MD</button>\n\t\t\t\t\t<button onclick=\"copyAll(this);\" name=\"xkx\" id=\"copyAll\"\n\t\t\t\t\t\tstyle=\"width:70px;background-color: #d0dbee;\">\n\t\t\t\t\t\t复制全部\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"list\"></div>\n\t\t</div>\n\t</div>\n\t<div id=\"footer\" style=\"position:fixed;width: 100%;text-align: center;bottom: 0px;display: none;\">\t\n\t\t<div style=\"height: 20px;\" >\n\t\t\t本站已开启鉴黄\n\t\t</div>\n\t\t<div style=\"height: 20px\" id=\"info\">\n\t\t</div>\n\t</div>\n</body>\n</html>"
  },
  {
    "path": "yopngs/wwwroot/static/file.js",
    "content": "/*\n * @xkx\n */\n$(() => {\n  let host = window.location.origin + '/api';\n\n  //$(document).ready(function () {\n  $.ajax({\n    url: host + '/supports',\n    type: 'get',\n    success: (res) => {\n      let selid = document.getElementById('type');\n      for (let i = 0; i < res.length; i++) {\n        selid.options[i] = new Option(res[i].name, res[i].type);\n      }\n    },\n  });\n\n  $.ajax({\n    url: host + '/info',\n    type: 'get',\n    success: (res) => {\n      let info = document.getElementById('info');\n        info.innerText = res;\n    },\n  });\n  \n  //  });\n  /* 临时粘贴上传 */\n  $(document).on('paste', (event) => {\n    let clipboardData =\n      event.clipboardData ||\n      window.clipboardData ||\n      event.originalEvent.clipboardData;\n    /* 判断是否支持粘贴上传 */\n    if (!clipboardData || !clipboardData.items)\n      return alert('当前浏览器不支持粘贴上传');\n    let items = clipboardData.items;\n    let file = null;\n    /* 判断剪切板的内容是否是桌面类型的文件 */\n    if (items.length === 0) return alert('剪切板内无内容或不支持桌面文件');\n    /* 开始循环剪切板的内容，判断是否是文件类型，如果是文件类型，则push */\n    for (let i = 0; i < items.length; i++) {\n      if (items[i].type.indexOf('image') !== -1) {\n        file = items[i].getAsFile();\n      }\n    }\n    if (!file) return alert('剪切板内无内容或不支持桌面文件');\n    upload(new Array(file));\n  });\n  /* 点击上传 */\n  $('.upload .content').on('click', function () {\n    $('#file').click();\n  });\n  /* 监听点击上传 */\n  $('#file').on('change', () => {\n    upload($('#file')[0].files);\n  });\n  /* 拖拽上传 */\n  $('#dragbox').on('dragover', (e) => {\n    e.preventDefault();\n  });\n  $('#dragbox').on('dragenter', (e) => {\n    e.preventDefault();\n    $('.upload').addClass('dragenter');\n  });\n  $('#dragbox').on('dragleave', (e) => {\n    e.preventDefault();\n    $('.upload').removeClass('dragenter');\n  });\n  $('#dragbox').on('drop', (e) => {\n    e.preventDefault();\n    $('.upload').removeClass('dragenter');\n    let files = e.originalEvent.dataTransfer.files;\n    upload(files);\n  });\n  /* 上传函数 */\n  function upload(files) {\n    //if ($('#type').val().trim() === '') return alert('请输入');\n    for (let i = 0; i < files.length; i++) {\n      var animateimg = files[i].name;\n      var imgarr = animateimg.split('\\\\');\n      var myimg = imgarr[imgarr.length - 1];\n      var houzui = myimg.lastIndexOf('.');\n      var ext = myimg.substring(houzui, myimg.length).toUpperCase();\n      var file = files[i];\n      if (!file) {\n        return false;\n      }\n      var fileSize = file.size;\n      var maxSize = 50 * 1024 * 1024;\n      if (\n        ext != '.PNG' &&\n        ext != '.GIF' &&\n        ext != '.JPG' &&\n        ext != '.JPEG' &&\n        ext != '.BMP'\n      ) {\n        parent.alert('文件类型错误,请上传图片类型');\n        $('#file').val(null);\n        return false;\n      } else if (parseInt(fileSize) >= parseInt(maxSize)) {\n        parent.alert('上传的文件不能超过' + maxSize / 1024 / 1024 + 'MB');\n        return false;\n      } else {\n        document.querySelector('.container').classList.add('start');\n        var type = $('#type').val();\n        var api = host + '/upload?type=';\n\n        let formData = new FormData();\n        formData.append('image', files[i]);\n        let randomClass = Date.now().toString(36);\n        $('.filelist .list').append(`\n\t\t\t\t<div class=\"item ${randomClass}\">\n\t\t\t\t\t<div class=\"file\">\n\t\t\t\t\t\t<svg class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t<path d=\"M961.536 531.251c-0.614-3.481-1.843-7.168-3.891-10.445L827.392 306.79v-28.876c0-10.445 0-20.276 0.205-29.901 0.819-89.703 1.433-174.285-101.376-179.2H303.923c-33.587 0-58.368 8.601-76.185 26.624-30.72 30.925-30.31 79.257-29.696 140.288 0 8.601 0.204 17.408 0.204 26.419v38.093c-2.867 2.253-5.324 4.915-7.168 8.192L64.717 523.879c-1.639 2.867-2.663 5.734-3.277 8.806-6.144 12.288-9.626 26.01-9.626 40.345v290.407c0 50.585 41.984 91.75 93.594 91.75h733.184c51.61 0 93.594-41.165 93.594-91.75V573.03c-0.205-14.95-4.096-29.286-10.65-41.779zM861.389 481.28h-33.997v-55.91l33.997 55.91zM271.565 138.65c5.53-5.53 16.384-8.397 32.358-8.397h420.045c36.25 1.843 42.803 11.264 41.78 117.145 0 9.83-0.206 19.866-0.206 30.516V481.28H664.576c-16.998 0-30.925 13.722-30.925 30.925 0 64.307-54.681 116.736-122.06 116.736S389.53 576.512 389.53 512.205c0-16.999-13.722-30.925-30.925-30.925H259.89V262.144c0-9.42 0-18.432-0.205-27.034-0.41-43.008-0.819-83.558 11.879-96.46z m-73.523 279.552v63.078h-36.864l36.864-63.078z m712.294 445.44c0 16.179-14.54 30.105-31.949 30.105H145.203c-17.203 0-31.949-13.721-31.949-30.105V573.03c0-16.179 14.541-30.105 31.95-30.105h185.548c15.155 83.763 90.522 147.456 181.043 147.456s165.888-63.898 181.043-147.456h185.55c17.202 0 31.948 13.721 31.948 30.105v290.612z\" p-id=\"14163\" fill=\"#909399\"></path>\n\t\t\t\t\t\t\t<path d=\"M385.638 278.528H655.77c16.998 0 30.924-13.722 30.924-30.925s-13.721-30.925-30.924-30.925H385.638c-16.998 0-30.924 13.722-30.924 30.925s13.926 30.925 30.924 30.925z m-30.924 70.451c0 16.999 13.721 30.925 30.924 30.925H655.77c16.998 0 30.924-13.722 30.924-30.925 0-17.203-13.721-30.925-30.924-30.925H385.638c-16.998 0-30.924 13.927-30.924 30.925z\" p-id=\"14164\" fill=\"#909399\"></path>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t<div class=\"desc\">\n\t\t\t\t\t\t\t<div class=\"desc__name\">${files[i].name}</div>\n\t\t\t\t\t\t\t<div class=\"desc__size\">SIZE:${formatBytes(files[i].size)}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<a href=\"javascript: void(0);\" class=\"link\">\n\t\t\t\t\t\t\t<svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t<path d=\"M82.603621 941.396379A280.234044 280.234044 0 0 1 0.001138 742.001267a280.063378 280.063378 0 0 1 82.659372-199.110669l145.919675-145.919676a46.535008 46.535008 0 0 1 65.820299 0 46.535008 46.535008 0 0 1 0 65.820298L148.480808 608.710896a187.732916 187.732916 0 0 0-55.352766 133.574814 187.732916 187.732916 0 0 0 55.352766 133.517926 187.732916 187.732916 0 0 0 133.574814 55.352766A187.732916 187.732916 0 0 0 415.289104 875.519192l145.919676-145.919676a46.535008 46.535008 0 0 1 65.820298 0 46.535008 46.535008 0 0 1 0 65.820299l-145.862787 145.919675A280.347821 280.347821 0 0 1 281.998733 1023.998862a280.120266 280.120266 0 0 1-199.395112-82.602483z m292.408239-292.465128a46.42123 46.42123 0 0 1 0-65.820298l208.099093-208.042204a46.478119 46.478119 0 0 1 65.820298 0 46.535008 46.535008 0 0 1 0 65.820298l-208.099093 208.042204a46.42123 46.42123 0 0 1-32.938593 13.653303 46.307453 46.307453 0 0 1-32.824816-13.653303z m354.587656-21.674618a46.535008 46.535008 0 0 1 0-65.820298L875.576081 415.289104a187.732916 187.732916 0 0 0 55.352766-133.517926 187.732916 187.732916 0 0 0-55.352766-133.517925 187.732916 187.732916 0 0 0-133.574814-55.352766 187.334695 187.334695 0 0 0-133.517926 55.352766L462.506777 294.172929a46.591896 46.591896 0 0 1-65.820299 0 46.591896 46.591896 0 0 1 0-65.820299l145.976565-145.919675A279.9496 279.9496 0 0 1 742.001267 0.001138a280.120266 280.120266 0 0 1 199.110668 82.602483A280.290933 280.290933 0 0 1 1023.998862 281.998733a280.120266 280.120266 0 0 1-82.602483 199.110669l-145.919676 145.919676a46.42123 46.42123 0 0 1-32.881704 13.596414 46.535008 46.535008 0 0 1-32.938594-13.368859z\" p-id=\"8129\" fill=\"#909399\"></path>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<a title=\"删除\" href=\"#\" class=\"link\" onclick=\"del(this);return false;\">\n\t\t\t\t\t\t\t<svg version=\"1.1\" id=\"Capa_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 50.13 50.13\" style=\"enable-background:new 0 0 50.13 50.13;\" xml:space=\"preserve\">\n\t\t\t\t\t\t\t\t<g>\n\t\t\t\t\t\t\t\t<path style=\"fill:#010002;\" d=\"M4.574,13.902h40.982c0.345,0,0.625-0.28,0.625-0.625V8.952c0-0.345-0.28-0.625-0.625-0.625H33.315\t\tV0.625C33.315,0.28,33.035,0,32.69,0H17.442c-0.345,0-0.625,0.28-0.625,0.625v7.702H4.574c-0.345,0-0.625,0.28-0.625,0.625v4.325 C3.949,13.622,4.23,13.902,4.574,13.902z M5.199,9.577h12.242c0.345,0,0.625-0.28,0.625-0.625V1.25h13.998v7.702\t\tc0,0.345,0.28,0.625,0.625,0.625h12.242v3.075H5.199V9.577z\"/>\n\t\t\t\t\t\t\t\t<path style=\"fill:#010002;\" d=\"M11.006,18.06H7.17c-0.345,0-0.625,0.28-0.625,0.625v30.82c0,0.345,0.28,0.625,0.625,0.625h35.791\tc0.345,0,0.625-0.28,0.625-0.625v-30.82c0-0.345-0.28-0.625-0.625-0.625h-3.836c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.537\tv-24.68c0-0.345-0.28-0.625-0.625-0.625h-4.324c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.535v-24.68\tc0-0.345-0.28-0.625-0.625-0.625h-4.324c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.537v-24.68c0-0.345-0.28-0.625-0.625-0.625\th-4.325c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.537v-24.68C11.631,18.34,11.351,18.06,11.006,18.06z M14.793,44.614\tc0.345,0,0.625-0.28,0.625-0.625V19.31h3.074v24.68c0,0.345,0.28,0.625,0.625,0.625h3.787c0.345,0,0.625-0.28,0.625-0.625V19.31\th3.074v24.68c0,0.345,0.28,0.625,0.625,0.625h3.785c0.345,0,0.625-0.28,0.625-0.625V19.31h3.074v24.68\tc0,0.345,0.28,0.625,0.625,0.625h3.787c0.345,0,0.625-0.28,0.625-0.625V19.31h2.586v29.57H7.795V19.31h2.586v24.68 c0,0.345,0.28,0.625,0.625,0.625L14.793,44.614L14.793,44.614z\"/>\n\t\t\t\t\t\t\t\t</g>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"progress\">\n\t\t\t\t\t\t<div class=\"progress-bar\">\n\t\t\t\t\t\t\t<div class=\"progress-inner\"></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"progress-status\">\n\t\t\t\t\t\t\t<svg class=\"icon status-success\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t<path d=\"M310.710857 768.292571z m402.578286 0z\" p-id=\"11696\" fill=\"#67C23A\"></path>\n\t\t\t\t\t\t\t\t<path d=\"M512 0C229.376 0 0 229.376 0 512S229.376 1024 512 1024 1024 794.624 1024 512 794.624 0 512 0z m300.178286 373.321143l-354.011429 354.011428c-21.065143 21.065143-55.588571 21.065143-76.653714 0l-169.691429-169.106285c-21.065143-21.065143-21.065143-55.588571 0-76.653715 21.065143-21.065143 55.588571-21.065143 76.653715 0l131.072 131.072 315.392-315.392c21.065143-21.065143 55.588571-21.065143 76.653714 0 21.650286 20.48 21.650286 55.003429 0.585143 76.068572z\" p-id=\"11697\" fill=\"#67C23A\"></path>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t<svg class=\"icon status-error\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t<path d=\"M733.678921 290.121702c22.43209 22.531789 22.43209 58.921624 0 81.353714L593.403583 511.850453 733.678921 652.125791c20.537825 20.537825 22.531789 53.13913 4.785513 76.069711l-4.785513 5.4834c-22.531789 22.531789-58.921624 22.531789-81.453412 0L511.95017 593.204167 371.674832 733.579204c-20.537825 20.537825-53.13913 22.531789-76.069711 4.785512l-5.383702-4.785512c-22.531789-22.531789-22.531789-58.921624 0-81.453413l140.275339-140.275338L290.321118 371.674813c-20.637523-20.537825-22.731185-53.13913-4.885211-76.169409l4.785512-5.383702c22.531789-22.43209 58.921624-22.43209 81.353715 0l140.275338 140.17564L652.225509 290.121702c20.537825-20.537825 53.039431-22.531789 75.970012-4.785513l5.4834 4.785513zM0.000019 511.850453c0 282.744037 229.206114 511.950151 511.950151 511.950151s511.950151-229.206114 511.950151-511.950151S794.694207 0 511.95017 0 0.000019 229.206114 0.000019 511.850453z\" p-id=\"14360\" fill=\"#F56C6C\"></path>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"result\" style=\"display:none\">\n\t\t\t\t\t<input value=\"\" id=\"Imgs_url\" >URL</input>\n\t\t\t\t\t<input value=\"\" id=\"Imgs_html\">HTML</input>\n\t\t\t\t\t<input value=\"\" id=\"Imgs_Ubb\">BBCode</input>\n\t\t\t\t\t<input value=\"\" id=\"Imgs_markdown\">Markdown</input>\n\t\t\t\t\t</div>\n\t\t\t\t\t<input id=\"show\" name=\"show\" onclick=\"oCopy(this)\" type=\"text\" value=\"\" readonly style=\"display:none\">\n\t\t\t\t</div>\n\t\t\t`);\n        $.ajax({\n          url: api + $('#type').val(),\n          type: 'post',\n          dataType: 'json',\n          processData: false,\n          contentType: false,\n\n          data: formData,\n          xhr: () => {\n            let xhr = $.ajaxSettings.xhr();\n            if (!xhr.upload) return;\n            xhr.upload.addEventListener(\n              'progress',\n              (e) => {\n                let percent = Math.floor((e.loaded / e.total) * 100);\n                $('.' + randomClass)\n                  .find('.progress-inner')\n                  .css('width', percent + '%');\n              },\n              false\n            );\n            return xhr;\n          },\n          success: (res) => {\n            if (type == 'muke') {\n              var imgSrc = res.data.url.muke;\n            } else if (type == 'vxichina') {\n              var imgSrc = res.data.url.vxichina;\n            } else if (type == 'qihoo') {\n              var imgSrc = res.data.url.qihoo;\n            } else if (type == 'catbox') {\n              var imgSrc = res.data.url.catbox;\n            } else if (type == 'gtimg') {\n              var imgSrc = res.data.url.gtimg;\n            } else {\n              var imgSrc = res.data.url;\n            }\n            /* 清除input框 */\n            $('#file').val(null);\n            if (res.code === -1) {\n              $('.' + randomClass).fadeOut();\n              alert(res.data.url);\n            } else {\n              if (res.code === 200) {\n                $('.' + randomClass)\n                  .find('.progress-inner')\n                  .addClass('success');\n                $('.' + randomClass)\n                  .find('.status-success')\n                  .show();\n                $('.' + randomClass)\n                  .find('.link')\n                  .attr({\n                    href: imgSrc,\n                    target: '_blank',\n                  });\n                //代码链接xkx\n                $('.' + randomClass)\n                  .find('#Imgs_url')\n                  .attr({\n                    value: imgSrc,\n                  });\n                $('.' + randomClass)\n                  .find('#Imgs_html')\n                  .attr({\n                    value: '<img src=\"' + imgSrc + '\"/>',\n                  });\n                $('.' + randomClass)\n                  .find('#Imgs_Ubb')\n                  .attr({\n                    value: '[img]' + imgSrc + '[/img]',\n                  });\n                $('.' + randomClass)\n                  .find('#Imgs_markdown')\n                  .attr({\n                    value: '![](' + imgSrc + ')',\n                  });\n                //显示链接xkx\n                $('.' + randomClass)\n                  .find('#show')\n                  .show();\n                $('.' + randomClass)\n                  .find('#show')\n                  .attr({\n                    value: imgSrc,\n                  });\n                //复制所有xkx\n                $('.copyall').show();\n                var tt = $('.filelist .title').html().replace('上传列表', '');\n                $('.filelist .title').html(tt);\n              } else {\n                $('.' + randomClass)\n                  .find('.progress-inner')\n                  .addClass('error');\n                $('.' + randomClass)\n                  .find('.status-error')\n                  .show();\n                $('.' + randomClass)\n                  .find('#show')\n                  .show();\n                $('.' + randomClass)\n                  .find('#show')\n                  .attr({\n                    value: res.msg,\n                  });\n              }\n            }\n          },\n          fail: () => {\n            $('.' + randomClass).fadeOut();\n          },\n        });\n      }\n    }\n  }\n  /* 获取文件大小 */\n  function formatBytes(bytes, decimals = 2) {\n    if (bytes === 0) return '0 Bytes';\n    const k = 1024;\n    const dm = decimals < 0 ? 0 : decimals;\n    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\n    const i = Math.floor(Math.log(bytes) / Math.log(k));\n    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + sizes[i];\n  }\n});\nfunction del(obj) {\n  var item = obj.parentNode.parentNode;\n  item.parentNode.removeChild(item);\n}\nfunction sel(obj) {\n  for (var i = 0; i < document.querySelectorAll('#Imgs' + obj.id).length; i++) {\n    document.querySelectorAll('#Imgs' + obj.id)[\n      i\n    ].parentElement.nextElementSibling.value = document.querySelectorAll(\n      '#Imgs' + obj.id\n    )[i].value;\n  }\n}\nfunction copyAll(obj) {\n  var xkx = '';\n  for (var i = 0; i < document.querySelectorAll('#show').length; i++) {\n    var xkx = xkx + document.querySelectorAll('#show')[i].value + '\\n';\n  }\n  var txa = document.createElement('textarea');\n  txa.value = xkx;\n  document.body.appendChild(txa);\n  txa.select();\n  var res = document.execCommand('copy');\n  document.body.removeChild(txa);\n  console.log('copy success');\n  console.log(xkx);\n  if (browserRedirect()) {\n    alert('设备类型为手机，有一定几率复制失败！请查看剪切板是否成功复制');\n  }\n}\nfunction oCopy(obj) {\n  obj.select();\n  document.execCommand('Copy');\n  console.log(obj.value);\n  if (browserRedirect()) {\n    alert('设备类型为手机，有一定几率复制失败！请查看剪切板是否成功复制');\n  }\n}\nfunction browserRedirect() {\n  var sUserAgent = navigator.userAgent.toLowerCase();\n  var bIsIpad = sUserAgent.match(/ipad/i) == 'ipad';\n  var bIsIphone = sUserAgent.match(/iphone os/i) == 'iphone os';\n  var bIsMidp = sUserAgent.match(/midp/i) == 'midp';\n  var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == 'rv:1.2.3.4';\n  var bIsUc = sUserAgent.match(/ucweb/i) == 'web';\n  var bIsCE = sUserAgent.match(/windows ce/i) == 'windows ce';\n  var bIsWM = sUserAgent.match(/windows mobile/i) == 'windows mobile';\n  var bIsAndroid = sUserAgent.match(/android/i) == 'android';\n  if (\n    bIsIpad ||\n    bIsIphone ||\n    bIsMidp ||\n    bIsUc7 ||\n    bIsUc ||\n    bIsCE ||\n    bIsWM ||\n    bIsAndroid\n  ) {\n    return 1;\n  }\n}\n"
  },
  {
    "path": "yopngs/wwwroot/static/style.css",
    "content": "* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n  -webkit-tap-highlight-color: transparent;\n}\n::-webkit-scrollbar {\n  width: 7px;\n  height: 7px;\n}\n::-webkit-scrollbar-thumb {\n  border-radius: 3.5px;\n  background-color: rgba(50, 50, 50, 0.3);\n}\n::-webkit-scrollbar-track {\n  border-radius: 3.5px;\n  background-color: rgba(50, 50, 50, 0.1);\n}\nhtml {\n  height: 100%;\n}\n@-webkit-keyframes Gradient {\n  0% {\n    background-position: 0% 50%;\n  }\n  50% {\n    background-position: 100% 50%;\n  }\n  100% {\n    background-position: 0% 50%;\n  }\n}\n@keyframes Gradient {\n  0% {\n    background-position: 0% 50%;\n  }\n  50% {\n    background-position: 100% 50%;\n  }\n  100% {\n    background-position: 0% 50%;\n  }\n}\nbody {\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: linear-gradient(\n    -45deg,\n    rgba(9, 69, 138, 0.2),\n    rgba(68, 155, 255, 0.7),\n    rgba(117, 113, 251, 0.7),\n    rgba(68, 155, 255, 0.7),\n    rgba(9, 69, 138, 0.2)\n  );\n  background-size: 400% 400%;\n  -webkit-animation: Gradient 15s ease infinite;\n  animation: Gradient 15s ease infinite;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',\n    'Microsoft YaHei', '微软雅黑', Arial, sans-serif;\n  font-size: 14px;\n}\n.container {\n  display: grid;\n  gap: 20px;\n  grid-template-columns: 500px 250px;\n  grid-template-rows: 500px;\n}\n.container.start {\n  display: grid;\n  gap: 20px;\n  grid-template-columns: 250px 500px;\n  grid-template-rows: 500px;\n}\n@-webkit-keyframes slideRight {\n  0% {\n    -webkit-transform: translateX(-50%);\n    transform: translateX(-50%);\n  }\n  100% {\n    -webkit-transform: translateX(0);\n    transform: translateX(0);\n  }\n}\n@keyframes slideRight {\n  0% {\n    -webkit-transform: translateX(-50%);\n    transform: translateX(-50%);\n  }\n  100% {\n    -webkit-transform: translateX(0);\n    transform: translateX(0);\n  }\n}\n@-webkit-keyframes slideLeft {\n  0% {\n    -webkit-transform: translateX(50%);\n    transform: translateX(50%);\n  }\n  100% {\n    -webkit-transform: translateX(0);\n    transform: translateX(0);\n  }\n}\n@keyframes slideLeft {\n  0% {\n    -webkit-transform: translateX(50%);\n    transform: translateX(50%);\n  }\n  100% {\n    -webkit-transform: translateX(0);\n    transform: translateX(0);\n  }\n}\n.upload,\n.filelist {\n  background: #fff;\n  border-radius: 8px;\n  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\n  overflow: hidden;\n}\n.upload .title,\n.filelist .title {\n  height: 45px;\n  line-height: 45px;\n  border-bottom: 1px solid #f2f6fc;\n  color: #606266;\n  font-weight: 500;\n  padding: 0 15px;\n  font-size: 16px;\n}\n.upload {\n  -webkit-animation: slideRight 1.5s;\n  animation: slideRight 1.5s;\n  transition: box-shadow 0.35s;\n}\n.upload .title {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n.copyall button {\n  width: 70px;\n  height: 28px;\n  border-radius: 14px;\n  margin: 0 12px;\n  background-color: #fff;\n}\n.upload .title select {\n  height: 28px;\n  padding: 0 5px;\n  border-radius: 14px;\n}\n.copyall {\n  display: inline-flex;\n  margin: 8px;\n}\n#show {\n  width: 475px;\n  height: 28px;\n  outline: none;\n  border: 1px solid #dcdfe6;\n  padding: 0 15px;\n  color: #606266;\n  border-radius: 14px;\n  font-size: 12px;\n  transition: border 0.35s;\n  -webkit-appearance: none;\n}\n.upload .title input:focus {\n  border-color: #409eff;\n}\n.upload .content {\n  cursor: pointer;\n  height: calc(100% - 45px);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n}\n.upload .content .icon {\n  width: 80px;\n  height: 80px;\n  margin-bottom: 20px;\n}\n.upload .content .desc {\n  color: #606266;\n}\n.upload.dragenter {\n  box-shadow: 20px 20px 20px 0 rgba(0, 0, 0, 0.25);\n}\n.upload.dragenter .content > * {\n  pointer-events: none;\n}\n.filelist {\n  -webkit-animation: slideLeft 1.5s;\n  animation: slideLeft 1.5s;\n}\n.filelist .list {\n  height: calc(100% - 45px);\n  overflow-y: auto;\n  padding: 10px;\n  scroll-behavior: smooth;\n}\n.filelist .list .item {\n  margin-bottom: 10px;\n}\n.filelist .list .item:last-child {\n  margin-bottom: 0;\n}\n.filelist .list .item .file {\n  display: flex;\n  align-items: center;\n  margin-bottom: 5px;\n}\n.filelist .list .item .file .icon {\n  width: 45px;\n  height: 45px;\n  margin-right: 10px;\n}\n.filelist .list .item .file .desc {\n  flex: 1;\n  min-width: 0;\n}\n.filelist .list .item .file .desc__name {\n  font-size: 15px;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  overflow: hidden;\n  font-weight: 600;\n  color: #606266;\n  margin-bottom: 5px;\n}\n.filelist .list .item .file .desc__size {\n  font-size: 12px;\n  color: #909399;\n}\n.filelist .list .item .file .link {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 5px;\n  cursor: pointer;\n  margin-left: 10px;\n  border-radius: 50%;\n  transition: background 0.2s;\n}\n.filelist .list .item .file .link svg {\n  width: 18px;\n  height: 18px;\n}\n.filelist .list .item .file .link:hover {\n  background: #f56c6c50;\n}\n.filelist .list .item .progress {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  font-size: 12px;\n}\n.filelist .list .item .progress .progress-bar {\n  flex: 1;\n  min-width: 0;\n}\n.filelist .list .item .progress .progress-bar .progress-inner {\n  width: 0%;\n  height: 2px;\n  background: #409eff;\n  border-radius: 1px;\n  transition: width 0.2s;\n}\n.filelist .list .item .progress .progress-bar .progress-inner.success {\n  background: #67c23a;\n}\n.filelist .list .item .progress .progress-bar .progress-inner.error {\n  background: #f56c6c;\n}\n.filelist .list .item .progress .progress-status {\n  margin-left: 5px;\n}\n.filelist .list .item .progress .progress-status .icon {\n  display: none;\n  width: 14px;\n  height: 14px;\n}\n@media (max-width: 780px) {\n  body {\n    height: auto;\n    padding: 5vh 0;\n  }\n  .container {\n    grid-template-columns: 350px;\n    grid-template-rows: 400px 150px;\n  }\n  .container.start {\n    grid-template-columns: 350px !important;\n    grid-template-rows: 190px 400px !important;\n  }\n  .copyall button {\n    width: 50px;\n    margin: 0 4px;\n  }\n  #show {\n    width: 320px;\n    padding: 0 11px;\n  }\n}\n"
  },
  {
    "path": "yopngs/yopngs.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp3.1</TargetFramework>\n    <UserSecretsId>508626fd-dc38-4a3f-9757-1caed8893c4f</UserSecretsId>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <AssemblyName>yopngs</AssemblyName>\n    <RootNamespace>yopngs</RootNamespace>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Aliyun.OSS.SDK.NetCore\" Version=\"2.13.0\" />\n    <PackageReference Include=\"AWSSDK.S3\" Version=\"3.7.7.5\" />\n    <PackageReference Include=\"B2Net\" Version=\"0.7.5\" />\n    <PackageReference Include=\"Kirinnee.Minimage\" Version=\"1.1.0\" />\n    <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.11.1\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.1\" />\n    <PackageReference Include=\"Octokit\" Version=\"0.50.0\" />\n    <PackageReference Include=\"Tencent.QCloud.Cos.Sdk\" Version=\"5.4.23\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"wwwroot\\static\\file.js\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Include=\"wwwroot\\index.html\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"wwwroot\\static\\style.css\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Update=\"defaultsetting.json\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <ItemGroup>\n    <None Update=\"pngQuant.bat\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"pngQuant.sh\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n  <ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema=\"\" /></VisualStudio></ProjectExtensions>\n</Project>\n"
  },
  {
    "path": "yopngs/yopngs.runtimeconfig.json",
    "content": "{\n  \"runtimeOptions\": {\n    \"tfm\": \"netcoreapp3.1\",\n    \"framework\": {\n      \"name\": \"Microsoft.AspNetCore.App\",\n      \"version\": \"3.1.0\"\n    },\n    \"configProperties\": {\n      \"System.GC.Server\": true,\n      \"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization\": false,\n      \"System.Net.Http.UseSocketsHttpHandler\": false\n    }\n  }\n}"
  },
  {
    "path": "yopngs.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.31129.286\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"yopngs\", \"yopngs\\yopngs.csproj\", \"{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {69890056-9C2F-480E-B9EE-2E12395A240B}\n\tEndGlobalSection\nEndGlobal\n"
  }
]