├── ClientApp
├── .browserslistrc
├── .prettierrc
├── src
│ ├── assets
│ │ └── background2.jpg
│ ├── reset.css
│ ├── index.jsx
│ ├── template.html
│ ├── api
│ │ └── index.js
│ ├── App.jsx
│ └── components
│ │ └── Ranking.jsx
├── .gitignore
├── webpack
│ ├── paths.js
│ ├── webpack.dev.js
│ ├── webpack.prod.js
│ └── webpack.common.js
├── webpack.config.js
├── .babelrc
├── LICENSE
├── .eslintrc
└── package.json
├── Caddyfile
├── Pages
├── _ViewImports.cshtml
├── Error.cshtml.cs
└── Error.cshtml
├── appsettings.json
├── appsettings.Development.json
├── .dockerignore
├── Dockerfile
├── Program.cs
├── .github
└── workflows
│ └── publish.yaml
├── ranking-showcase.csproj
├── Controllers
└── AnimeController.cs
├── Startup.cs
├── Services
├── RankingMgr.cs
└── FileSync.cs
└── .gitignore
/ClientApp/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 0.5%
2 | last 2 versions
3 | Firefox ESR
4 | not dead
5 |
--------------------------------------------------------------------------------
/ClientApp/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
5 |
--------------------------------------------------------------------------------
/Caddyfile:
--------------------------------------------------------------------------------
1 | https://ranking.ikely.me {
2 | proxy / http://app:5000 {
3 | transparent
4 | websocket
5 | }
6 | }
--------------------------------------------------------------------------------
/ClientApp/src/assets/background2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wattlebird/rankit-showcase/HEAD/ClientApp/src/assets/background2.jpg
--------------------------------------------------------------------------------
/ClientApp/src/reset.css:
--------------------------------------------------------------------------------
1 | .ant-layout {
2 | background-color: #020111;
3 | }
4 |
5 | .ant-layout-header {
6 | background-color: transparent;
7 | }
8 |
--------------------------------------------------------------------------------
/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using ranking_showcase
2 | @namespace ranking_showcase.Pages
3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
4 |
--------------------------------------------------------------------------------
/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | out/
4 | .git/
5 | .gitignore/
6 | .vscode/
7 | Pipeline/
8 | caddy.log
9 | customrank_*.csv
10 | caddy
11 | Caddyfile
12 |
13 | #JS
14 | ClientApp/build/
15 | ClientApp/node_modules/
16 | ClientApp/*.md
--------------------------------------------------------------------------------
/ClientApp/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import zhCN from 'antd/lib/locale-provider/zh_CN';
4 | import { LocaleProvider } from 'antd';
5 | import App from './App.jsx';
6 |
7 | import 'reset.css';
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('app'),
14 | );
15 |
--------------------------------------------------------------------------------
/ClientApp/.gitignore:
--------------------------------------------------------------------------------
1 | # Cruft
2 | *~
3 | *#
4 | .#*
5 | .DS_Store
6 |
7 | # Test
8 | coverage
9 | .eslintcache
10 |
11 | # Logs
12 | *.log
13 |
14 | # npm & yarn
15 | node_modules/
16 | .npm
17 | .yarn-integrity
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 | yarn.lock*
22 | package-lock.json
23 | /build/bundle.js
24 | /build/index.html
25 | /build*
26 | /build/*
27 | # Editor/IDE
28 | .idea
29 | packages/api/etc/
30 |
--------------------------------------------------------------------------------
/ClientApp/webpack/paths.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | root: path.resolve(__dirname, '../'),
5 | outputPath: path.resolve(__dirname, '../', 'build'),
6 | entryPath: path.resolve(__dirname, '../', 'src/index.jsx'),
7 | templatePath: path.resolve(__dirname, '../', 'src/template.html'),
8 | imagesFolder: 'images',
9 | fontsFolder: 'fonts',
10 | cssFolder: 'css',
11 | jsFolder: 'js',
12 | };
13 |
--------------------------------------------------------------------------------
/ClientApp/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Bangumi Research - Scientific animation ranking
7 |
8 |
9 |
10 | You need to enable JavaScript to run this app.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ClientApp/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpackMerge = require('webpack-merge');
2 | const common = require('./webpack/webpack.common');
3 |
4 | const envs = {
5 | development: 'dev',
6 | production: 'prod',
7 | };
8 | /* eslint-disable global-require,import/no-dynamic-require */
9 | const env = envs[process.env.NODE_ENV || 'development'];
10 | const envConfig = require(`./webpack/webpack.${env}.js`);
11 | module.exports = webpackMerge(common, envConfig);
12 |
--------------------------------------------------------------------------------
/ClientApp/src/api/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const baseUrl = '/api/anime';
4 |
5 | function ReadAll(start = 0, len = 50000) {
6 | return axios
7 | .get(`${baseUrl}/list`, {
8 | params: {
9 | start,
10 | length: len,
11 | },
12 | })
13 | .then(data => data.data);
14 | }
15 |
16 | function Search(q) {
17 | return axios.get(`${baseUrl}/search`, {
18 | params: {
19 | q,
20 | },
21 | });
22 | }
23 |
24 | function Date() {
25 | return axios.get(`${baseUrl}/date`).then(data => data.data);
26 | }
27 |
28 | export default {
29 | ReadAll,
30 | Search,
31 | Date,
32 | };
33 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM microsoft/dotnet:2.1-sdk AS build
2 | WORKDIR /app
3 |
4 | # copy csproj and restore as distinct layers
5 | COPY *.cs *.csproj *.json ./
6 | COPY ClientApp ./ClientApp
7 | COPY Controllers ./Controllers
8 | COPY Pages ./Pages
9 | COPY Services ./Services
10 | RUN dotnet restore
11 |
12 | RUN curl -sL https://deb.nodesource.com/setup_10.x | bash && \
13 | apt-get install -y nodejs
14 |
15 | # copy everything else and build app
16 | RUN dotnet publish -c Release -o out
17 |
18 |
19 | FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
20 | WORKDIR /app
21 | COPY --from=build /app/out ./
22 | EXPOSE 5000
23 | ENTRYPOINT ["dotnet", "ranking-showcase.dll"]
24 |
--------------------------------------------------------------------------------
/Pages/Error.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.RazorPages;
8 |
9 | namespace ranking_showcase.Pages
10 | {
11 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
12 | public class ErrorModel : PageModel
13 | {
14 | public string RequestId { get; set; }
15 |
16 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
17 |
18 | public void OnGet()
19 | {
20 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace ranking_showcase
12 | {
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | CreateWebHostBuilder(args).Build().Run();
18 | }
19 |
20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
21 | WebHost.CreateDefaultBuilder(args)
22 | .UseStartup().UseUrls("http://0.0.0.0:5000");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Pages/Error.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ErrorModel
3 | @{
4 | ViewData["Title"] = "Error";
5 | }
6 |
7 | Error.
8 | An error occurred while processing your request.
9 |
10 | @if (Model.ShowRequestId)
11 | {
12 |
13 | Request ID: @Model.RequestId
14 |
15 | }
16 |
17 | Development Mode
18 |
19 | Swapping to Development environment will display more detailed information about the error that occurred.
20 |
21 |
22 | Development environment should not be enabled in deployed applications , as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development , and restarting the application.
23 |
24 |
--------------------------------------------------------------------------------
/ClientApp/webpack/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 |
3 | const commonPaths = require('./paths');
4 |
5 | module.exports = {
6 | mode: 'development',
7 | output: {
8 | filename: '[name].js',
9 | path: commonPaths.outputPath,
10 | chunkFilename: '[name].js',
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.css$/,
16 | use: [
17 | 'style-loader',
18 | {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: true,
22 | // modules: true,
23 | // camelCase: true,
24 | // localIdentName: '[local]___[hash:base64:5]',
25 | },
26 | },
27 | ],
28 | },
29 | ],
30 | },
31 | devServer: {
32 | contentBase: commonPaths.outputPath,
33 | compress: true,
34 | hot: true,
35 | port: 3000,
36 | },
37 | plugins: [new webpack.HotModuleReplacementPlugin()],
38 | };
39 |
--------------------------------------------------------------------------------
/ClientApp/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"],
3 | "plugins": [
4 | "@babel/plugin-transform-runtime",
5 | "react-hot-loader/babel",
6 |
7 | // Stage 2 https://github.com/babel/babel/tree/master/packages/babel-preset-stage-2
8 | ["@babel/plugin-proposal-decorators", {
9 | "legacy": true
10 | }],
11 | "@babel/plugin-proposal-function-sent",
12 | "@babel/plugin-proposal-export-namespace-from",
13 | "@babel/plugin-proposal-numeric-separator",
14 | "@babel/plugin-proposal-throw-expressions",
15 |
16 | // Stage 3
17 | "@babel/plugin-syntax-dynamic-import",
18 | "@babel/plugin-syntax-import-meta",
19 | ["@babel/plugin-proposal-class-properties", {
20 | "loose": true
21 | }],
22 | "@babel/plugin-proposal-json-strings",
23 | ["import", {
24 | "libraryName": "antd",
25 | "libraryDirectory": "es",
26 | "style": "css" // `style: true` 会加载 less 文件
27 | }]
28 | ]
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/ClientApp/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Hashem Khalifa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yaml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI/CD
2 |
3 | on:
4 | push:
5 | # Publish `master` as Docker `latest` image.
6 | branches:
7 | - master
8 |
9 | # Publish `v1.2.3` tags as releases.
10 | tags:
11 | - v*
12 |
13 | jobs:
14 | publish:
15 |
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v1
20 | - name: Build the Docker image
21 | run: docker build . --tag image
22 | - name: Log into registry
23 | run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u wattlebird --password-stdin
24 |
25 | - name: Push image
26 | run: |
27 | IMAGE_ID=${{ github.repository }}
28 | # Strip git ref prefix from version
29 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
30 | # Strip "v" prefix from tag name
31 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
32 | # Use Docker `latest` tag convention
33 | [ "$VERSION" == "master" ] && VERSION=latest
34 | echo IMAGE_ID=$IMAGE_ID
35 | echo VERSION=$VERSION
36 | docker tag image $IMAGE_ID:$VERSION
37 | docker push $IMAGE_ID:$VERSION
--------------------------------------------------------------------------------
/ClientApp/webpack/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
2 | const CleanWebpackPlugin = require('clean-webpack-plugin');
3 | const commonPaths = require('./paths');
4 |
5 | module.exports = {
6 | mode: 'production',
7 | output: {
8 | filename: `${commonPaths.jsFolder}/[name].[hash].js`,
9 | path: commonPaths.outputPath,
10 | chunkFilename: '[name].[chunkhash].js',
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.css$/,
16 | use: [
17 | MiniCssExtractPlugin.loader,
18 | {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: true,
22 | // modules: true,
23 | // camelCase: true,
24 | // localIdentName: '[local]___[hash:base64:5]',
25 | },
26 | },
27 | ],
28 | },
29 | ],
30 | },
31 | plugins: [
32 | new CleanWebpackPlugin([commonPaths.outputPath.split('/').pop()], {
33 | root: commonPaths.root,
34 | }),
35 | new MiniCssExtractPlugin({
36 | filename: `${commonPaths.cssFolder}/[name].css`,
37 | chunkFilename: '[id].css',
38 | }),
39 | ],
40 | devtool: 'source-map',
41 | };
42 |
--------------------------------------------------------------------------------
/ClientApp/webpack/webpack.common.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const convert = require('koa-connect');
3 | const history = require('connect-history-api-fallback');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const commonPaths = require('./paths');
6 |
7 | module.exports = {
8 | entry: commonPaths.entryPath,
9 | module: {
10 | rules: [
11 | {
12 | enforce: 'pre',
13 | test: /\.(js|jsx)$/,
14 | loader: 'eslint-loader',
15 | exclude: /(node_modules)/,
16 | options: {
17 | emitWarning: process.env.NODE_ENV !== 'production',
18 | },
19 | },
20 | {
21 | test: /\.(js|jsx)$/,
22 | loader: 'babel-loader',
23 | exclude: /(node_modules)/,
24 | },
25 | {
26 | test: /\.(png|jpg|gif|svg)$/,
27 | use: [
28 | {
29 | loader: 'file-loader',
30 | options: {
31 | outputPath: commonPaths.imagesFolder,
32 | },
33 | },
34 | ],
35 | },
36 | {
37 | test: /\.(woff2|ttf|woff|eot)$/,
38 | use: [
39 | {
40 | loader: 'file-loader',
41 | options: {
42 | outputPath: commonPaths.fontsFolder,
43 | },
44 | },
45 | ],
46 | },
47 | ],
48 | },
49 | serve: {
50 | add: app => {
51 | app.use(convert(history()));
52 | },
53 | content: commonPaths.entryPath,
54 | dev: {
55 | publicPath: commonPaths.outputPath,
56 | },
57 | open: true,
58 | },
59 | resolve: {
60 | modules: ['src', 'node_modules'],
61 | extensions: ['*', '.js', '.jsx', '.css', '.scss'],
62 | },
63 | plugins: [
64 | new webpack.ProgressPlugin(),
65 | new HtmlWebpackPlugin({
66 | template: commonPaths.templatePath,
67 | }),
68 | ],
69 | };
70 |
--------------------------------------------------------------------------------
/ClientApp/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "plugins": ["react", "prettier"],
4 | "extends": ["airbnb", "plugin:prettier/recommended", "prettier/react"],
5 | "globals": {
6 | "React": true,
7 | "document": true,
8 | "window": true,
9 | "jQuery": true,
10 | "$": true,
11 | "localStorage": true,
12 | "fetch": true
13 | },
14 | "root": true,
15 | "rules": {
16 | "indent": ["error", 2],
17 | "react/prefer-stateless-function": "warn",
18 | "react/self-closing-comp": [
19 | "warn",
20 | {
21 | "component": true,
22 | "html": false
23 | }
24 | ],
25 | "react/sort-comp": [
26 | 1,
27 | {
28 | "order": [
29 | "static-methods",
30 | "lifecycle",
31 | "everything-else",
32 | "rendering"
33 | ],
34 | "groups": {
35 | "rendering": ["/^render.+$/", "render"]
36 | }
37 | }
38 | ],
39 | "react/require-default-props": 0,
40 | "jsx-a11y/href-no-hash": "off",
41 | "jsx-a11y/anchor-is-valid": ["warn", { "aspects": ["invalidHref"] }],
42 | "react/jsx-boolean-value": ["warn", "never"],
43 | "react/jsx-closing-bracket-location": ["warn", "after-props"],
44 | "react/jsx-curly-spacing": ["warn", "never"],
45 | "react/jsx-filename-extension": ["warn", { "extensions": [".jsx"] }],
46 | "react/jsx-first-prop-new-line": ["warn", "multiline"],
47 | "react/jsx-handler-names": [
48 | "warn",
49 | {
50 | "eventHandlerPrefix": "handle",
51 | "eventHandlerPropPrefix": "on"
52 | }
53 | ],
54 | "react/jsx-indent": ["warn", 2],
55 | "react/jsx-key": "error",
56 | "react/jsx-wrap-multilines": ["warn"],
57 | "react/jsx-indent-props": 0,
58 | "no-trailing-spaces": [2, { "skipBlankLines": true }],
59 | "prefer-template": 0,
60 | "import/prefer-default-export": 0,
61 | "import/no-unresolved": 0,
62 | "import/no-extraneous-dependencies": 0,
63 | "import/extensions": 0,
64 | "babel/object-curly-spacing": 0,
65 | "prettier/prettier": "warn"
66 | },
67 | "env": {
68 | "es6": true,
69 | "browser": true,
70 | "node": true
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/ranking-showcase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | true
6 | Latest
7 | false
8 | ClientApp\
9 | $(DefaultItemExcludes);$(SpaRoot)node_modules\**
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | %(DistFiles.Identity)
45 | PreserveNewest
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Controllers/AnimeController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using ranking_showcase.Service;
7 |
8 | namespace ranking_showcase.Controllers
9 | {
10 | [Route("api/[controller]")]
11 | public class AnimeController : Controller
12 | {
13 | private RankingMgr rankingMgr;
14 | public AnimeController(RankingMgr rankingMgr) {
15 | this.rankingMgr = rankingMgr;
16 | }
17 |
18 | // Should match /api/anime/list, /api/anime/list?start=20&length=20
19 | [HttpGet("list")]
20 | public IActionResult ReadAll()
21 | {
22 | try{
23 | int start = String.IsNullOrEmpty(Request.Query["start"]) ? 0 : Convert.ToInt32(Request.Query["start"]);
24 | int len = String.IsNullOrEmpty(Request.Query["length"]) ? 100000 : Convert.ToInt32(Request.Query["length"]);
25 | return Json(rankingMgr.readAll(start, len));
26 | } catch (Exception e) {
27 | return BadRequest(e.Message);
28 | }
29 |
30 | }
31 |
32 | // Should match /api/anime/id/x
33 | [HttpGet("id/{id}")]
34 | public IActionResult ReadById(int id) {
35 | try {
36 | return Json(rankingMgr.readById(id));
37 | } catch (Exception e) {
38 | return BadRequest(e.Message);
39 | }
40 | }
41 |
42 | // Should match /api/anime/rank/5, /api/anime/rank/5?source=original
43 | [HttpGet("rank/{rank}")]
44 | public IActionResult ReadByRank(int rank) {
45 | try {
46 | string source = Request.Query["source"];
47 | if (source == "original") {
48 | return Json(rankingMgr.readByBgmrank(rank));
49 | }
50 | else {
51 | return Json(rankingMgr.readByRank(rank));
52 | }
53 | } catch (Exception e) {
54 | return BadRequest(e.Message);
55 | }
56 | }
57 |
58 | [HttpGet("search")]
59 | public IActionResult Search() {
60 | try {
61 | string q = Request.Query["q"];
62 | if (!string.IsNullOrEmpty(q)) {
63 | return Json(rankingMgr.searchByName(q));
64 | }else {
65 | return Json(new List{});
66 | }
67 | } catch (Exception e) {
68 | return BadRequest(e.Message);
69 | }
70 | }
71 |
72 | [HttpGet("date")]
73 | public string Date() {
74 | return rankingMgr.readUpdateDate().ToString("yyyy-MM-dd");
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/ClientApp/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { hot } from 'react-hot-loader';
3 | import { Layout, Icon, Button } from 'antd';
4 | import styled from 'styled-components';
5 | import backgroundImg from 'assets/background2.jpg';
6 | import Ranking from './components/Ranking';
7 |
8 | const { Header, Content, Footer } = Layout;
9 |
10 | const CustomHeader = styled(Header)`
11 | color: rgba(255, 255, 255, 0.8);
12 | background-color: transparent;
13 | padding: 0 4em;
14 | height: 64px;
15 | display: flex;
16 | justify-content: space-between;
17 | align-items: center;
18 | font-size: 24px;
19 | `;
20 |
21 | const TitleBox = styled.div`
22 | margin: 8em auto;
23 | text-align: center;
24 | color: rgba(255, 255, 255, 0.9);
25 | `;
26 |
27 | const Title = styled.div`
28 | font-size: 36px;
29 | margin-bottom: 1em;
30 | `;
31 |
32 | const TitleNote = styled.span`
33 | font-size: 12px;
34 | `;
35 |
36 | const ButtonGroup = styled.div`
37 | font-size: 24px;
38 | display: flex;
39 | justify-content: center;
40 | `;
41 |
42 | const SubTitleButton = styled(Button)`
43 | margin-left: 8px;
44 | &.ant-btn {
45 | border-radius: 0;
46 | }
47 | :first-child {
48 | margin-left: 0;
49 | }
50 | `;
51 |
52 | const CustomFooter = styled(Footer)`
53 | &.ant-layout-footer {
54 | text-align: center;
55 | background-color: #020111;
56 | color: rgba(255, 255, 255, 0.8);
57 | }
58 | `;
59 |
60 | class App extends Component {
61 | constructor(props) {
62 | super(props);
63 | this.state = {};
64 | }
65 |
66 | render() {
67 | return (
68 |
74 |
75 | Bangumi Research
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | 某科学的 Bangumi 动画排名
84 | v0.999
85 |
86 |
87 | 使用说明
88 |
91 | GitHub 页面
92 |
93 |
94 |
95 |
96 |
97 |
98 | © 2018 Ronnie Wang, all rights reserved. Powered by React and .Net
99 | Core on Linux.
100 |
101 |
102 | );
103 | }
104 | }
105 |
106 | export default hot(module)(App);
107 |
--------------------------------------------------------------------------------
/ClientApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rankit-showcase",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "cross-env NODE_ENV=development webpack-dev-server --open",
8 | "build": "cross-env NODE_ENV=production webpack",
9 | "format": "prettier --write 'packages/**/*.js'"
10 | },
11 | "repository": "https://github.com/wattlebird/rankit-showcase",
12 | "author": "Ronnie Wang",
13 | "private": false,
14 | "engines": {
15 | "node": ">=8",
16 | "npm": ">=3"
17 | },
18 | "dependencies": {
19 | "antd": "^3.10.7",
20 | "axios": "^0.18.0",
21 | "lodash": "4.17.15",
22 | "npm-check-updates": "^2.14.2",
23 | "prop-types": "15.6.2",
24 | "raf": "^3.4.1",
25 | "react": "16.6.0",
26 | "react-dom": "16.6.0",
27 | "react-hot-loader": "4.3.11",
28 | "react-router": "^4.3.1",
29 | "styled-components": "^4.1.1"
30 | },
31 | "resolutions": {
32 | "babel-core": "7.0.0-bridge.0"
33 | },
34 | "devDependencies": {
35 | "@babel/core": "7.1.2",
36 | "@babel/plugin-proposal-class-properties": "7.1.0",
37 | "@babel/plugin-proposal-decorators": "7.1.2",
38 | "@babel/plugin-proposal-export-namespace-from": "7.0.0",
39 | "@babel/plugin-proposal-function-sent": "7.1.0",
40 | "@babel/plugin-proposal-json-strings": "7.0.0",
41 | "@babel/plugin-proposal-numeric-separator": "7.0.0",
42 | "@babel/plugin-proposal-throw-expressions": "7.0.0",
43 | "@babel/plugin-syntax-dynamic-import": "7.0.0",
44 | "@babel/plugin-syntax-import-meta": "7.0.0",
45 | "@babel/plugin-transform-runtime": "7.1.0",
46 | "@babel/polyfill": "7.0.0",
47 | "@babel/preset-env": "7.1.0",
48 | "@babel/preset-react": "7.0.0",
49 | "@babel/register": "7.0.0",
50 | "@babel/runtime": "7.1.2",
51 | "babel-eslint": "10.0.1",
52 | "babel-loader": "8.0.4",
53 | "babel-plugin-import": "^1.11.0",
54 | "babel-plugin-lodash": "3.3.4",
55 | "browserslist": "4.3.4",
56 | "clean-webpack-plugin": "0.1.19",
57 | "connect-history-api-fallback": "^1.5.0",
58 | "cross-env": "5.2.0",
59 | "css-loader": "1.0.0",
60 | "eslint": "5.8.0",
61 | "eslint-config-airbnb": "17.1.0",
62 | "eslint-config-prettier": "3.1.0",
63 | "eslint-loader": "2.1.1",
64 | "eslint-plugin-import": "2.14.0",
65 | "eslint-plugin-jsx-a11y": "6.1.2",
66 | "eslint-plugin-prettier": "3.0.0",
67 | "eslint-plugin-react": "7.11.1",
68 | "eslint-watch": "4.0.2",
69 | "file-loader": "2.0.0",
70 | "html-webpack-plugin": "3.2.0",
71 | "identity-obj-proxy": "3.0.0",
72 | "jsdom": "^12.0.0",
73 | "koa-connect": "^2.0.1",
74 | "lint-staged": "7.3.0",
75 | "mini-css-extract-plugin": "0.4.4",
76 | "prettier": "1.14.3",
77 | "pretty-quick": "1.8.0",
78 | "sass-loader": "7.1.0",
79 | "style-loader": "0.23.1",
80 | "webpack": "4.23.1",
81 | "webpack-cli": "3.1.2",
82 | "webpack-dev-server": "3.10.1",
83 | "webpack-merge": "4.1.4"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.AspNetCore.HttpsPolicy;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.Extensions.FileProviders;
9 | using ranking_showcase.Service;
10 |
11 | namespace ranking_showcase
12 | {
13 | public class Startup
14 | {
15 | public Startup(IConfiguration configuration, IHostingEnvironment environment)
16 | {
17 | Configuration = configuration;
18 | this.environment = environment;
19 | }
20 |
21 | public IConfiguration Configuration { get; }
22 | private readonly IHostingEnvironment environment;
23 |
24 | // This method gets called by the runtime. Use this method to add services to the container.
25 | public void ConfigureServices(IServiceCollection services)
26 | {
27 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
28 | var physicalProvider = environment.ContentRootFileProvider;
29 | services.AddSingleton(physicalProvider);
30 | services.AddSingleton();
31 | services.AddHostedService();
32 | services.AddCors(options => {
33 | options.AddPolicy("AllowAllOrigins", builder => builder.AllowAnyOrigin());
34 | });
35 |
36 | // In production, the React files will be served from this directory
37 | services.AddSpaStaticFiles(configuration =>
38 | {
39 | configuration.RootPath = "ClientApp/build";
40 | });
41 | }
42 |
43 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
44 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
45 | {
46 | if (env.IsDevelopment())
47 | {
48 | app.UseDeveloperExceptionPage();
49 | }
50 | else
51 | {
52 | app.UseExceptionHandler("/Error");
53 | //app.UseHsts();
54 | }
55 |
56 | //app.UseHttpsRedirection();
57 | app.UseStaticFiles();
58 | app.UseSpaStaticFiles();
59 | app.UseCors("AllowAllOrigins");
60 |
61 | app.UseMvc(routes =>
62 | {
63 | routes.MapRoute(
64 | name: "default",
65 | template: "{controller}/{action=Index}/{id?}");
66 | });
67 |
68 | app.UseSpa(spa =>
69 | {
70 | spa.Options.SourcePath = "ClientApp";
71 |
72 | if (env.IsDevelopment())
73 | {
74 | spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");
75 | }
76 | });
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Services/RankingMgr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using Microsoft.Extensions.FileProviders;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace ranking_showcase.Service {
9 | public class RankingItm {
10 | public int id {get;set;}
11 | public int rank {get;set;}
12 | public int bgmrank {get;set;}
13 | public String name {get;set;}
14 | }
15 | public class RankingMgr {
16 | private List rankingItms = new List();
17 | private IFileProvider _fileProvider;
18 | private Regex rgx = new Regex(@"customrank_(\d+)_(\d+)_(\d+).csv", RegexOptions.Compiled);
19 | public string currentFile {get; private set;}
20 | public RankingMgr(IFileProvider fileProvider) {
21 | this._fileProvider = fileProvider;
22 | this.refresh(null);
23 | }
24 |
25 | public void refresh(string fileName) {
26 | if (String.IsNullOrEmpty(fileName)) return;
27 | var rankingFile = this._fileProvider.GetFileInfo(fileName);
28 | if (rankingFile.Exists) {
29 | this.rankingItms.Clear();
30 | currentFile = fileName;
31 | using(StreamReader stm = new StreamReader(rankingFile.CreateReadStream())) {
32 | string line;
33 | stm.ReadLine();
34 | while ((line = stm.ReadLine()) != null) {
35 | var rec = line.Split(',', 4);
36 | rankingItms.Add(new RankingItm{
37 | id = Convert.ToInt32(rec[0]),
38 | rank = Convert.ToInt32(rec[1]),
39 | bgmrank = Convert.ToInt32(rec[2]),
40 | name = rec[3]
41 | });
42 | }
43 | }
44 | } else {
45 | throw new FileNotFoundException($"{fileName} not found in current directory.");
46 | }
47 |
48 | }
49 |
50 | public IEnumerable readAll(int begin=0, int len=50000) {
51 | return rankingItms.Skip(begin).Take(len);
52 | }
53 |
54 | public RankingItm readById(int id) {
55 | return rankingItms.Where(itm => itm.id == id).FirstOrDefault();
56 | }
57 |
58 | public RankingItm readByRank(int rank) {
59 | return rankingItms.Where(itm => itm.rank == rank).FirstOrDefault();
60 | }
61 |
62 | public RankingItm readByBgmrank(int bgmrank) {
63 | return rankingItms.Where(itm => itm.bgmrank == bgmrank).FirstOrDefault();
64 | }
65 |
66 | public IEnumerable searchByName(string name) {
67 | return rankingItms.Where(itm => itm.name.Contains(name));
68 | }
69 |
70 | public DateTime readUpdateDate() {
71 | var matcher = this.rgx.Match(this.currentFile);
72 | if (matcher.Success) {
73 | return new DateTime(Convert.ToInt32(matcher.Groups[1].Value), Convert.ToInt32(matcher.Groups[2].Value), Convert.ToInt32(matcher.Groups[3].Value));
74 | } else {
75 | return new DateTime();
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/Services/FileSync.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Microsoft.Extensions.FileProviders;
8 | using Microsoft.Extensions.Hosting;
9 | using Microsoft.WindowsAzure;
10 | using Microsoft.WindowsAzure.Storage;
11 | using Microsoft.WindowsAzure.Storage.File;
12 | using System.Text.RegularExpressions;
13 |
14 | namespace ranking_showcase.Service {
15 | public class FileSync : IHostedService, IDisposable {
16 | private Timer _timer;
17 | private Regex rgx = new Regex(@"customrank_(\d+)_(\d+)_(\d+).csv", RegexOptions.Compiled);
18 | private IFileProvider _fileProvider;
19 | private RankingMgr _rankingMgr;
20 |
21 | public FileSync(
22 | IFileProvider fileProvider,
23 | RankingMgr rankingMgr
24 | ) {
25 | this._fileProvider = fileProvider;
26 | this._rankingMgr = rankingMgr;
27 | }
28 |
29 | public Task StartAsync(CancellationToken cancellationToken) {
30 | _timer = new Timer(this.DownloadFileWrapper, null, TimeSpan.Zero, TimeSpan.FromHours(1));
31 | return Task.CompletedTask;
32 | }
33 |
34 | public Task StopAsync(CancellationToken cancellationToken) {
35 | _timer?.Change(Timeout.Infinite, Timeout.Infinite);
36 | return Task.CompletedTask;
37 | }
38 |
39 | private void DownloadFileWrapper(object state) {
40 | this.DownloadFile().GetAwaiter().GetResult();
41 | }
42 |
43 | private async Task DownloadFile() {
44 | CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
45 | Environment.GetEnvironmentVariable("AZURE_STORAGE_IKELY_CONNECTIONSTRING")
46 | );
47 | CloudFileClient fileClient = storageAccount.CreateCloudFileClient();
48 | CloudFileShare share = fileClient.GetShareReference("bangumi-publish");
49 | CloudFileDirectory dir = share.GetRootDirectoryReference().GetDirectoryReference("ranking");
50 | FileContinuationToken token = null;
51 | DateTime date = new DateTime(2018, 1, 1);
52 | do
53 | {
54 | FileResultSegment resultSegment = await dir.ListFilesAndDirectoriesSegmentedAsync(token);
55 | token = resultSegment.ContinuationToken;
56 | foreach (IListFileItem rankItm in resultSegment.Results)
57 | {
58 | string fileName = rankItm.Uri.ToString();
59 | var match = rgx.Match(fileName);
60 | if (match.Success) {
61 | DateTime newdate = new DateTime(Convert.ToInt32(match.Groups[1].Value), Convert.ToInt32(match.Groups[2].Value), Convert.ToInt32(match.Groups[3].Value));
62 | if (newdate.CompareTo(date) > 0) {
63 | date = newdate;
64 | }
65 | }
66 | }
67 | }
68 | while (token != null);
69 | if (date.CompareTo(new DateTime(2018, 1, 1) ) != 0) {
70 | string rankingFile = $"customrank_{date.ToString("yyyy_MM_dd")}.csv";
71 | CloudFile fr = dir.GetFileReference(rankingFile);
72 | // One should check whether there's an existing file
73 | bool ex = await fr.ExistsAsync();
74 | if (ex) {
75 | await fr.DownloadToFileAsync(rankingFile, FileMode.Create);
76 | }
77 | if (this._rankingMgr.currentFile != rankingFile) {
78 | this._rankingMgr.refresh(rankingFile);
79 | }
80 | }
81 | }
82 |
83 | public void Dispose() {
84 | _timer?.Dispose();
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/ClientApp/src/components/Ranking.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card, Table, Input, message } from 'antd';
3 | import styled from 'styled-components';
4 | import raf from 'raf';
5 | import API from '../api';
6 |
7 | const Content = styled(Card).attrs({
8 | bordered: false,
9 | })`
10 | width: 67vw;
11 | &.ant-card {
12 | margin: 4em auto 2em;
13 | background-color: rgba(255, 255, 255, 0.85);
14 | color: #111517;
15 | }
16 | `;
17 |
18 | const SearchBar = styled.div`
19 | display: flex;
20 | align-items: baseline;
21 | margin-bottom: 1em;
22 | `;
23 |
24 | const DateLabel = styled.div`
25 | flex-shrink: 0;
26 | margin-left: 1em;
27 | `;
28 |
29 | const easeInOutCubic = (s, b, c, d) => {
30 | let t = s;
31 | const cc = c - b;
32 | t /= d / 2;
33 | if (t < 1) {
34 | return (cc / 2) * t * t * t + b;
35 | }
36 | t -= 2;
37 | return (cc / 2) * (t * t * t + 2) + b;
38 | };
39 |
40 | class Ranking extends React.Component {
41 | state = {
42 | dataSource: [],
43 | updateDate: null,
44 | isLoading: true,
45 | name: null,
46 | };
47 |
48 | componentDidMount() {
49 | this.setState({
50 | isLoading: true,
51 | });
52 | const promises = [API.ReadAll(), API.Date()];
53 | Promise.all(promises).then(
54 | data => {
55 | this.setState({
56 | dataSource: data[0],
57 | updateDate: data[1],
58 | isLoading: false,
59 | });
60 | },
61 | err => {
62 | this.setState({
63 | isLoading: false,
64 | });
65 | message.error('加载失败,可能是网络问题,请稍后重试。');
66 | console.log(err);
67 | },
68 | );
69 | }
70 |
71 | onSearch = e => {
72 | this.setState({
73 | name: e.target.value,
74 | });
75 | };
76 |
77 | onScrollToTop = () => {
78 | const self = document.getElementById('content');
79 | const pageScrollTop =
80 | window.pageYOffset || document.documentElement.scrollTop;
81 | const contentScrollTop = self.offsetTop;
82 | if (contentScrollTop < pageScrollTop) {
83 | const start = Date.now();
84 | const frame = () => {
85 | const current = Date.now();
86 | const delta = current - start;
87 | const next = easeInOutCubic(
88 | delta,
89 | pageScrollTop,
90 | contentScrollTop,
91 | 450,
92 | );
93 | document.body.scrollTop = next;
94 | document.documentElement.scrollTop = next;
95 | if (delta < 450) {
96 | raf(frame);
97 | } else {
98 | document.body.scrollTop = contentScrollTop;
99 | document.documentElement.scrollTop = contentScrollTop;
100 | }
101 | };
102 | raf(frame);
103 | }
104 | };
105 |
106 | render() {
107 | const { name, updateDate, dataSource, isLoading } = this.state;
108 | const parser = new DOMParser();
109 | const columns = [
110 | {
111 | title: '番剧',
112 | dataIndex: 'name',
113 | key: 'name',
114 | render: (itm, rec) => (
115 |
120 | {parser.parseFromString(itm, 'text/html').body.textContent}
121 |
122 | ),
123 | },
124 | {
125 | title: '本站排名',
126 | dataIndex: 'rank',
127 | key: 'rank',
128 | sorter: (a, b) => a.rank - b.rank,
129 | width: 120,
130 | },
131 | {
132 | title: 'Bangumi 番组计划排名',
133 | dataIndex: 'bgmrank',
134 | key: 'bgmrank',
135 | sorter: (a, b) => a.bgmrank - b.bgmrank,
136 | width: 240,
137 | },
138 | ];
139 | return (
140 |
141 |
142 |
146 | {updateDate && (
147 |
148 | 排名更新时间:
149 | {updateDate}
150 |
151 | )}
152 |
153 | itm.name.includes(name))
157 | : dataSource
158 | }
159 | columns={columns}
160 | loading={isLoading}
161 | pagination={{
162 | pageSize: 20,
163 | pageSizeOptions: ['20', '50', '100'],
164 | showQuickJumper: true,
165 | showSizeChanger: true,
166 | onChange: this.onScrollToTop,
167 | }}
168 | />
169 |
170 | );
171 | }
172 | }
173 |
174 | export default Ranking;
175 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /Properties/launchSettings.json
2 |
3 | ## Ignore Visual Studio temporary files, build results, and
4 | ## files generated by popular Visual Studio add-ons.
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | build/
23 | bld/
24 | bin/
25 | Bin/
26 | obj/
27 | Obj/
28 | out/
29 |
30 | # Visual Studio 2015 cache/options directory
31 | .vs/
32 | /wwwroot/dist/
33 |
34 | # MSTest test Results
35 | [Tt]est[Rr]esult*/
36 | [Bb]uild[Ll]og.*
37 |
38 | # NUNIT
39 | *.VisualState.xml
40 | TestResult.xml
41 |
42 | # Build Results of an ATL Project
43 | [Dd]ebugPS/
44 | [Rr]eleasePS/
45 | dlldata.c
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | orleans.codegen.cs
185 |
186 | /node_modules
187 |
188 | # RIA/Silverlight projects
189 | Generated_Code/
190 |
191 | # Backup & report files from converting an old project file
192 | # to a newer Visual Studio version. Backup files are not needed,
193 | # because we have git ;-)
194 | _UpgradeReport_Files/
195 | Backup*/
196 | UpgradeLog*.XML
197 | UpgradeLog*.htm
198 |
199 | # SQL Server files
200 | *.mdf
201 | *.ldf
202 |
203 | # Business Intelligence projects
204 | *.rdl.data
205 | *.bim.layout
206 | *.bim_*.settings
207 |
208 | # Microsoft Fakes
209 | FakesAssemblies/
210 |
211 | # GhostDoc plugin setting file
212 | *.GhostDoc.xml
213 |
214 | # Node.js Tools for Visual Studio
215 | .ntvs_analysis.dat
216 |
217 | # Visual Studio 6 build log
218 | *.plg
219 |
220 | # Visual Studio 6 workspace options file
221 | *.opt
222 |
223 | # Visual Studio LightSwitch build output
224 | **/*.HTMLClient/GeneratedArtifacts
225 | **/*.DesktopClient/GeneratedArtifacts
226 | **/*.DesktopClient/ModelManifest.xml
227 | **/*.Server/GeneratedArtifacts
228 | **/*.Server/ModelManifest.xml
229 | _Pvt_Extensions
230 |
231 | # Paket dependency manager
232 | .paket/paket.exe
233 |
234 | # FAKE - F# Make
235 | .fake/
236 |
237 | # Project related
238 | /.vscode/
239 | *.csv
240 | !customrank.csv
241 | *.tsv
242 |
243 | # Docker
244 | docker-compose.yml
--------------------------------------------------------------------------------