mirror of
https://github.com/tgbot-collection/YYeTsBot.git
synced 2025-11-25 03:15:05 +08:00
add favourite
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -10,8 +10,9 @@ web/css/** linguist-vendored
|
||||
web/fonts/* linguist-vendored
|
||||
web/img/* linguist-vendored
|
||||
web/js/* linguist-vendored
|
||||
web/js/analytics.js -linguist-vendored
|
||||
web/404.html linguist-vendored
|
||||
web/resource.html linguist-vendored
|
||||
|
||||
web/js/common.js -linguist-vendored
|
||||
|
||||
tests/data/* linguist-vendored
|
||||
|
||||
@@ -50,6 +50,9 @@ yyets_offline - 人人影视离线数据
|
||||
本网站永久免费,并且没有任何限制。
|
||||

|
||||
|
||||
支持收藏功能,会跨设备同步
|
||||

|
||||
|
||||
## 指定字幕组搜索
|
||||
|
||||
目前只支持YYeTsOffline和ZimuxiaOnline
|
||||
|
||||
BIN
assets/2.png
BIN
assets/2.png
Binary file not shown.
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 314 KiB |
BIN
assets/like.png
Normal file
BIN
assets/like.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 113 KiB |
697
web/css/noty.css
vendored
Normal file
697
web/css/noty.css
vendored
Normal file
@@ -0,0 +1,697 @@
|
||||
.noty_layout_mixin, #noty_layout__top, #noty_layout__topLeft, #noty_layout__topCenter, #noty_layout__topRight, #noty_layout__bottom, #noty_layout__bottomLeft, #noty_layout__bottomCenter, #noty_layout__bottomRight, #noty_layout__center, #noty_layout__centerLeft, #noty_layout__centerRight {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: 9999999;
|
||||
-webkit-transform: translateZ(0) scale(1, 1);
|
||||
transform: translateZ(0) scale(1, 1);
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
filter: blur(0);
|
||||
-webkit-filter: blur(0);
|
||||
max-width: 90%; }
|
||||
|
||||
#noty_layout__top {
|
||||
top: 0;
|
||||
left: 5%;
|
||||
width: 90%; }
|
||||
|
||||
#noty_layout__topLeft {
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
width: 325px; }
|
||||
|
||||
#noty_layout__topCenter {
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
width: 325px;
|
||||
-webkit-transform: translate(-webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1);
|
||||
transform: translate(calc(-50% - .5px)) translateZ(0) scale(1, 1); }
|
||||
|
||||
#noty_layout__topRight {
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 325px; }
|
||||
|
||||
#noty_layout__bottom {
|
||||
bottom: 0;
|
||||
left: 5%;
|
||||
width: 90%; }
|
||||
|
||||
#noty_layout__bottomLeft {
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
width: 325px; }
|
||||
|
||||
#noty_layout__bottomCenter {
|
||||
bottom: 5%;
|
||||
left: 50%;
|
||||
width: 325px;
|
||||
-webkit-transform: translate(-webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1);
|
||||
transform: translate(calc(-50% - .5px)) translateZ(0) scale(1, 1); }
|
||||
|
||||
#noty_layout__bottomRight {
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 325px; }
|
||||
|
||||
#noty_layout__center {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 325px;
|
||||
-webkit-transform: translate(-webkit-calc(-50% - .5px), -webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1);
|
||||
transform: translate(calc(-50% - .5px), calc(-50% - .5px)) translateZ(0) scale(1, 1); }
|
||||
|
||||
#noty_layout__centerLeft {
|
||||
top: 50%;
|
||||
left: 20px;
|
||||
width: 325px;
|
||||
-webkit-transform: translate(0, -webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1);
|
||||
transform: translate(0, calc(-50% - .5px)) translateZ(0) scale(1, 1); }
|
||||
|
||||
#noty_layout__centerRight {
|
||||
top: 50%;
|
||||
right: 20px;
|
||||
width: 325px;
|
||||
-webkit-transform: translate(0, -webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1);
|
||||
transform: translate(0, calc(-50% - .5px)) translateZ(0) scale(1, 1); }
|
||||
|
||||
.noty_progressbar {
|
||||
display: none; }
|
||||
|
||||
.noty_has_timeout.noty_has_progressbar .noty_progressbar {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 3px;
|
||||
width: 100%;
|
||||
background-color: #646464;
|
||||
opacity: 0.2;
|
||||
filter: alpha(opacity=10); }
|
||||
|
||||
.noty_bar {
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform: translate(0, 0) translateZ(0) scale(1, 1);
|
||||
-ms-transform: translate(0, 0) scale(1, 1);
|
||||
transform: translate(0, 0) scale(1, 1);
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
overflow: hidden; }
|
||||
|
||||
.noty_effects_open {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate(50%);
|
||||
-ms-transform: translate(50%);
|
||||
transform: translate(50%);
|
||||
-webkit-animation: noty_anim_in 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
animation: noty_anim_in 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards; }
|
||||
|
||||
.noty_effects_close {
|
||||
-webkit-animation: noty_anim_out 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
animation: noty_anim_out 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards; }
|
||||
|
||||
.noty_fix_effects_height {
|
||||
-webkit-animation: noty_anim_height 75ms ease-out;
|
||||
animation: noty_anim_height 75ms ease-out; }
|
||||
|
||||
.noty_close_with_click {
|
||||
cursor: pointer; }
|
||||
|
||||
.noty_close_button {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
font-weight: bold;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
-webkit-transition: all .2s ease-out;
|
||||
transition: all .2s ease-out; }
|
||||
|
||||
.noty_close_button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1); }
|
||||
|
||||
.noty_modal {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
z-index: 10000;
|
||||
opacity: .3;
|
||||
left: 0;
|
||||
top: 0; }
|
||||
|
||||
.noty_modal.noty_modal_open {
|
||||
opacity: 0;
|
||||
-webkit-animation: noty_modal_in .3s ease-out;
|
||||
animation: noty_modal_in .3s ease-out; }
|
||||
|
||||
.noty_modal.noty_modal_close {
|
||||
-webkit-animation: noty_modal_out .3s ease-out;
|
||||
animation: noty_modal_out .3s ease-out;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards; }
|
||||
|
||||
@-webkit-keyframes noty_modal_in {
|
||||
100% {
|
||||
opacity: .3; } }
|
||||
|
||||
@keyframes noty_modal_in {
|
||||
100% {
|
||||
opacity: .3; } }
|
||||
|
||||
@-webkit-keyframes noty_modal_out {
|
||||
100% {
|
||||
opacity: 0; } }
|
||||
|
||||
@keyframes noty_modal_out {
|
||||
100% {
|
||||
opacity: 0; } }
|
||||
|
||||
@keyframes noty_modal_out {
|
||||
100% {
|
||||
opacity: 0; } }
|
||||
|
||||
@-webkit-keyframes noty_anim_in {
|
||||
100% {
|
||||
-webkit-transform: translate(0);
|
||||
transform: translate(0);
|
||||
opacity: 1; } }
|
||||
|
||||
@keyframes noty_anim_in {
|
||||
100% {
|
||||
-webkit-transform: translate(0);
|
||||
transform: translate(0);
|
||||
opacity: 1; } }
|
||||
|
||||
@-webkit-keyframes noty_anim_out {
|
||||
100% {
|
||||
-webkit-transform: translate(50%);
|
||||
transform: translate(50%);
|
||||
opacity: 0; } }
|
||||
|
||||
@keyframes noty_anim_out {
|
||||
100% {
|
||||
-webkit-transform: translate(50%);
|
||||
transform: translate(50%);
|
||||
opacity: 0; } }
|
||||
|
||||
@-webkit-keyframes noty_anim_height {
|
||||
100% {
|
||||
height: 0; } }
|
||||
|
||||
@keyframes noty_anim_height {
|
||||
100% {
|
||||
height: 0; } }
|
||||
|
||||
.noty_theme__relax.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
position: relative; }
|
||||
.noty_theme__relax.noty_bar .noty_body {
|
||||
padding: 10px; }
|
||||
.noty_theme__relax.noty_bar .noty_buttons {
|
||||
border-top: 1px solid #e7e7e7;
|
||||
padding: 5px 10px; }
|
||||
|
||||
.noty_theme__relax.noty_type__alert,
|
||||
.noty_theme__relax.noty_type__notification {
|
||||
background-color: #fff;
|
||||
border: 1px solid #dedede;
|
||||
color: #444; }
|
||||
|
||||
.noty_theme__relax.noty_type__warning {
|
||||
background-color: #FFEAA8;
|
||||
border: 1px solid #FFC237;
|
||||
color: #826200; }
|
||||
.noty_theme__relax.noty_type__warning .noty_buttons {
|
||||
border-color: #dfaa30; }
|
||||
|
||||
.noty_theme__relax.noty_type__error {
|
||||
background-color: #FF8181;
|
||||
border: 1px solid #e25353;
|
||||
color: #FFF; }
|
||||
.noty_theme__relax.noty_type__error .noty_buttons {
|
||||
border-color: darkred; }
|
||||
|
||||
.noty_theme__relax.noty_type__info,
|
||||
.noty_theme__relax.noty_type__information {
|
||||
background-color: #78C5E7;
|
||||
border: 1px solid #3badd6;
|
||||
color: #FFF; }
|
||||
.noty_theme__relax.noty_type__info .noty_buttons,
|
||||
.noty_theme__relax.noty_type__information .noty_buttons {
|
||||
border-color: #0B90C4; }
|
||||
|
||||
.noty_theme__relax.noty_type__success {
|
||||
background-color: #BCF5BC;
|
||||
border: 1px solid #7cdd77;
|
||||
color: darkgreen; }
|
||||
.noty_theme__relax.noty_type__success .noty_buttons {
|
||||
border-color: #50C24E; }
|
||||
|
||||
.noty_theme__metroui.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-shadow: rgba(0, 0, 0, 0.298039) 0 0 5px 0; }
|
||||
.noty_theme__metroui.noty_bar .noty_progressbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 3px;
|
||||
width: 100%;
|
||||
background-color: #000;
|
||||
opacity: 0.2;
|
||||
filter: alpha(opacity=20); }
|
||||
.noty_theme__metroui.noty_bar .noty_body {
|
||||
padding: 1.25em;
|
||||
font-size: 14px; }
|
||||
.noty_theme__metroui.noty_bar .noty_buttons {
|
||||
padding: 0 10px .5em 10px; }
|
||||
|
||||
.noty_theme__metroui.noty_type__alert,
|
||||
.noty_theme__metroui.noty_type__notification {
|
||||
background-color: #fff;
|
||||
color: #1d1d1d; }
|
||||
|
||||
.noty_theme__metroui.noty_type__warning {
|
||||
background-color: #FA6800;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__metroui.noty_type__error {
|
||||
background-color: #CE352C;
|
||||
color: #FFF; }
|
||||
|
||||
.noty_theme__metroui.noty_type__info,
|
||||
.noty_theme__metroui.noty_type__information {
|
||||
background-color: #1BA1E2;
|
||||
color: #FFF; }
|
||||
|
||||
.noty_theme__metroui.noty_type__success {
|
||||
background-color: #60A917;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__mint.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
position: relative; }
|
||||
.noty_theme__mint.noty_bar .noty_body {
|
||||
padding: 10px;
|
||||
font-size: 14px; }
|
||||
.noty_theme__mint.noty_bar .noty_buttons {
|
||||
padding: 10px; }
|
||||
|
||||
.noty_theme__mint.noty_type__alert,
|
||||
.noty_theme__mint.noty_type__notification {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #D1D1D1;
|
||||
color: #2F2F2F; }
|
||||
|
||||
.noty_theme__mint.noty_type__warning {
|
||||
background-color: #FFAE42;
|
||||
border-bottom: 1px solid #E89F3C;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__mint.noty_type__error {
|
||||
background-color: #DE636F;
|
||||
border-bottom: 1px solid #CA5A65;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__mint.noty_type__info,
|
||||
.noty_theme__mint.noty_type__information {
|
||||
background-color: #7F7EFF;
|
||||
border-bottom: 1px solid #7473E8;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__mint.noty_type__success {
|
||||
background-color: #AFC765;
|
||||
border-bottom: 1px solid #A0B55C;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__sunset.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
position: relative; }
|
||||
.noty_theme__sunset.noty_bar .noty_body {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); }
|
||||
.noty_theme__sunset.noty_bar .noty_buttons {
|
||||
padding: 10px; }
|
||||
|
||||
.noty_theme__sunset.noty_type__alert,
|
||||
.noty_theme__sunset.noty_type__notification {
|
||||
background-color: #073B4C;
|
||||
color: #fff; }
|
||||
.noty_theme__sunset.noty_type__alert .noty_progressbar,
|
||||
.noty_theme__sunset.noty_type__notification .noty_progressbar {
|
||||
background-color: #fff; }
|
||||
|
||||
.noty_theme__sunset.noty_type__warning {
|
||||
background-color: #FFD166;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__sunset.noty_type__error {
|
||||
background-color: #EF476F;
|
||||
color: #fff; }
|
||||
.noty_theme__sunset.noty_type__error .noty_progressbar {
|
||||
opacity: .4; }
|
||||
|
||||
.noty_theme__sunset.noty_type__info,
|
||||
.noty_theme__sunset.noty_type__information {
|
||||
background-color: #118AB2;
|
||||
color: #fff; }
|
||||
.noty_theme__sunset.noty_type__info .noty_progressbar,
|
||||
.noty_theme__sunset.noty_type__information .noty_progressbar {
|
||||
opacity: .6; }
|
||||
|
||||
.noty_theme__sunset.noty_type__success {
|
||||
background-color: #06D6A0;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__bootstrap-v3.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px; }
|
||||
.noty_theme__bootstrap-v3.noty_bar .noty_body {
|
||||
padding: 15px; }
|
||||
.noty_theme__bootstrap-v3.noty_bar .noty_buttons {
|
||||
padding: 10px; }
|
||||
.noty_theme__bootstrap-v3.noty_bar .noty_close_button {
|
||||
font-size: 21px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
filter: alpha(opacity=20);
|
||||
opacity: .2;
|
||||
background: transparent; }
|
||||
.noty_theme__bootstrap-v3.noty_bar .noty_close_button:hover {
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
filter: alpha(opacity=50);
|
||||
opacity: .5; }
|
||||
|
||||
.noty_theme__bootstrap-v3.noty_type__alert,
|
||||
.noty_theme__bootstrap-v3.noty_type__notification {
|
||||
background-color: #fff;
|
||||
color: inherit; }
|
||||
|
||||
.noty_theme__bootstrap-v3.noty_type__warning {
|
||||
background-color: #fcf8e3;
|
||||
color: #8a6d3b;
|
||||
border-color: #faebcc; }
|
||||
|
||||
.noty_theme__bootstrap-v3.noty_type__error {
|
||||
background-color: #f2dede;
|
||||
color: #a94442;
|
||||
border-color: #ebccd1; }
|
||||
|
||||
.noty_theme__bootstrap-v3.noty_type__info,
|
||||
.noty_theme__bootstrap-v3.noty_type__information {
|
||||
background-color: #d9edf7;
|
||||
color: #31708f;
|
||||
border-color: #bce8f1; }
|
||||
|
||||
.noty_theme__bootstrap-v3.noty_type__success {
|
||||
background-color: #dff0d8;
|
||||
color: #3c763d;
|
||||
border-color: #d6e9c6; }
|
||||
|
||||
.noty_theme__bootstrap-v4.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
border-radius: .25rem; }
|
||||
.noty_theme__bootstrap-v4.noty_bar .noty_body {
|
||||
padding: .75rem 1.25rem; }
|
||||
.noty_theme__bootstrap-v4.noty_bar .noty_buttons {
|
||||
padding: 10px; }
|
||||
.noty_theme__bootstrap-v4.noty_bar .noty_close_button {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
filter: alpha(opacity=20);
|
||||
opacity: .5;
|
||||
background: transparent; }
|
||||
.noty_theme__bootstrap-v4.noty_bar .noty_close_button:hover {
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
filter: alpha(opacity=50);
|
||||
opacity: .75; }
|
||||
|
||||
.noty_theme__bootstrap-v4.noty_type__alert,
|
||||
.noty_theme__bootstrap-v4.noty_type__notification {
|
||||
background-color: #fff;
|
||||
color: inherit; }
|
||||
|
||||
.noty_theme__bootstrap-v4.noty_type__warning {
|
||||
background-color: #fcf8e3;
|
||||
color: #8a6d3b;
|
||||
border-color: #faebcc; }
|
||||
|
||||
.noty_theme__bootstrap-v4.noty_type__error {
|
||||
background-color: #f2dede;
|
||||
color: #a94442;
|
||||
border-color: #ebccd1; }
|
||||
|
||||
.noty_theme__bootstrap-v4.noty_type__info,
|
||||
.noty_theme__bootstrap-v4.noty_type__information {
|
||||
background-color: #d9edf7;
|
||||
color: #31708f;
|
||||
border-color: #bce8f1; }
|
||||
|
||||
.noty_theme__bootstrap-v4.noty_type__success {
|
||||
background-color: #dff0d8;
|
||||
color: #3c763d;
|
||||
border-color: #d6e9c6; }
|
||||
|
||||
.noty_theme__semanticui.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
font-size: 1em;
|
||||
border-radius: .28571429rem;
|
||||
box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.22) inset, 0 0 0 0 transparent; }
|
||||
.noty_theme__semanticui.noty_bar .noty_body {
|
||||
padding: 1em 1.5em;
|
||||
line-height: 1.4285em; }
|
||||
.noty_theme__semanticui.noty_bar .noty_buttons {
|
||||
padding: 10px; }
|
||||
|
||||
.noty_theme__semanticui.noty_type__alert,
|
||||
.noty_theme__semanticui.noty_type__notification {
|
||||
background-color: #f8f8f9;
|
||||
color: rgba(0, 0, 0, 0.87); }
|
||||
|
||||
.noty_theme__semanticui.noty_type__warning {
|
||||
background-color: #fffaf3;
|
||||
color: #573a08;
|
||||
box-shadow: 0 0 0 1px #c9ba9b inset, 0 0 0 0 transparent; }
|
||||
|
||||
.noty_theme__semanticui.noty_type__error {
|
||||
background-color: #fff6f6;
|
||||
color: #9f3a38;
|
||||
box-shadow: 0 0 0 1px #e0b4b4 inset, 0 0 0 0 transparent; }
|
||||
|
||||
.noty_theme__semanticui.noty_type__info,
|
||||
.noty_theme__semanticui.noty_type__information {
|
||||
background-color: #f8ffff;
|
||||
color: #276f86;
|
||||
box-shadow: 0 0 0 1px #a9d5de inset, 0 0 0 0 transparent; }
|
||||
|
||||
.noty_theme__semanticui.noty_type__success {
|
||||
background-color: #fcfff5;
|
||||
color: #2c662d;
|
||||
box-shadow: 0 0 0 1px #a3c293 inset, 0 0 0 0 transparent; }
|
||||
|
||||
.noty_theme__nest.noty_bar {
|
||||
margin: 0 0 15px 0;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
box-shadow: rgba(0, 0, 0, 0.098039) 5px 4px 10px 0; }
|
||||
.noty_theme__nest.noty_bar .noty_body {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); }
|
||||
.noty_theme__nest.noty_bar .noty_buttons {
|
||||
padding: 10px; }
|
||||
|
||||
.noty_layout .noty_theme__nest.noty_bar {
|
||||
z-index: 5; }
|
||||
|
||||
.noty_layout .noty_theme__nest.noty_bar:nth-child(2) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-top: 4px;
|
||||
margin-right: -4px;
|
||||
margin-left: 4px;
|
||||
z-index: 4;
|
||||
width: 100%; }
|
||||
|
||||
.noty_layout .noty_theme__nest.noty_bar:nth-child(3) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-top: 8px;
|
||||
margin-right: -8px;
|
||||
margin-left: 8px;
|
||||
z-index: 3;
|
||||
width: 100%; }
|
||||
|
||||
.noty_layout .noty_theme__nest.noty_bar:nth-child(4) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-top: 12px;
|
||||
margin-right: -12px;
|
||||
margin-left: 12px;
|
||||
z-index: 2;
|
||||
width: 100%; }
|
||||
|
||||
.noty_layout .noty_theme__nest.noty_bar:nth-child(5) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-top: 16px;
|
||||
margin-right: -16px;
|
||||
margin-left: 16px;
|
||||
z-index: 1;
|
||||
width: 100%; }
|
||||
|
||||
.noty_layout .noty_theme__nest.noty_bar:nth-child(n+6) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-top: 20px;
|
||||
margin-right: -20px;
|
||||
margin-left: 20px;
|
||||
z-index: -1;
|
||||
width: 100%; }
|
||||
|
||||
#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(2),
|
||||
#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(2) {
|
||||
margin-top: 4px;
|
||||
margin-left: -4px;
|
||||
margin-right: 4px; }
|
||||
|
||||
#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(3),
|
||||
#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(3) {
|
||||
margin-top: 8px;
|
||||
margin-left: -8px;
|
||||
margin-right: 8px; }
|
||||
|
||||
#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(4),
|
||||
#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(4) {
|
||||
margin-top: 12px;
|
||||
margin-left: -12px;
|
||||
margin-right: 12px; }
|
||||
|
||||
#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(5),
|
||||
#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(5) {
|
||||
margin-top: 16px;
|
||||
margin-left: -16px;
|
||||
margin-right: 16px; }
|
||||
|
||||
#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(n+6),
|
||||
#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(n+6) {
|
||||
margin-top: 20px;
|
||||
margin-left: -20px;
|
||||
margin-right: 20px; }
|
||||
|
||||
.noty_theme__nest.noty_type__alert,
|
||||
.noty_theme__nest.noty_type__notification {
|
||||
background-color: #073B4C;
|
||||
color: #fff; }
|
||||
.noty_theme__nest.noty_type__alert .noty_progressbar,
|
||||
.noty_theme__nest.noty_type__notification .noty_progressbar {
|
||||
background-color: #fff; }
|
||||
|
||||
.noty_theme__nest.noty_type__warning {
|
||||
background-color: #FFD166;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__nest.noty_type__error {
|
||||
background-color: #EF476F;
|
||||
color: #fff; }
|
||||
.noty_theme__nest.noty_type__error .noty_progressbar {
|
||||
opacity: .4; }
|
||||
|
||||
.noty_theme__nest.noty_type__info,
|
||||
.noty_theme__nest.noty_type__information {
|
||||
background-color: #118AB2;
|
||||
color: #fff; }
|
||||
.noty_theme__nest.noty_type__info .noty_progressbar,
|
||||
.noty_theme__nest.noty_type__information .noty_progressbar {
|
||||
opacity: .6; }
|
||||
|
||||
.noty_theme__nest.noty_type__success {
|
||||
background-color: #06D6A0;
|
||||
color: #fff; }
|
||||
|
||||
.noty_theme__light.noty_bar {
|
||||
margin: 4px 0;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
position: relative; }
|
||||
.noty_theme__light.noty_bar .noty_body {
|
||||
padding: 10px; }
|
||||
.noty_theme__light.noty_bar .noty_buttons {
|
||||
border-top: 1px solid #e7e7e7;
|
||||
padding: 5px 10px; }
|
||||
|
||||
.noty_theme__light.noty_type__alert,
|
||||
.noty_theme__light.noty_type__notification {
|
||||
background-color: #fff;
|
||||
border: 1px solid #dedede;
|
||||
color: #444; }
|
||||
|
||||
.noty_theme__light.noty_type__warning {
|
||||
background-color: #FFEAA8;
|
||||
border: 1px solid #FFC237;
|
||||
color: #826200; }
|
||||
.noty_theme__light.noty_type__warning .noty_buttons {
|
||||
border-color: #dfaa30; }
|
||||
|
||||
.noty_theme__light.noty_type__error {
|
||||
background-color: #ED7000;
|
||||
border: 1px solid #e25353;
|
||||
color: #FFF; }
|
||||
.noty_theme__light.noty_type__error .noty_buttons {
|
||||
border-color: darkred; }
|
||||
|
||||
.noty_theme__light.noty_type__info,
|
||||
.noty_theme__light.noty_type__information {
|
||||
background-color: #78C5E7;
|
||||
border: 1px solid #3badd6;
|
||||
color: #FFF; }
|
||||
.noty_theme__light.noty_type__info .noty_buttons,
|
||||
.noty_theme__light.noty_type__information .noty_buttons {
|
||||
border-color: #0B90C4; }
|
||||
|
||||
.noty_theme__light.noty_type__success {
|
||||
background-color: #57C880;
|
||||
border: 1px solid #7cdd77;
|
||||
color: darkgreen; }
|
||||
.noty_theme__light.noty_type__success .noty_buttons {
|
||||
border-color: #50C24E; }
|
||||
|
||||
/*# sourceMappingURL=noty.css.map*/
|
||||
@@ -83,12 +83,18 @@
|
||||
1. 你的IP地址会被记录在Nginx的日志中。在面临攻击、爬虫等恶意行为时,我会找到这个IP然后加到防火墙中。我不会公开或与第三方分享访问日志;<br>
|
||||
2. 我使用了Google Analytics,请参考Google Analytics的隐私政策;<br>
|
||||
3. 我使用了Cloudflare,请参考Cloudflare的隐私政策。<br>
|
||||
3. 我记录了metrics信息,用于优化日后访问量,此信息不包含个人信息,无法用于追踪你。可以<a href="/api/metrics">点击这里查看</a>。
|
||||
4. 我记录了metrics信息,用于优化日后访问量,此信息不包含个人信息,无法用于追踪你。可以<a href="/api/metrics">点击这里查看</a>。
|
||||
5. 如果你选择注册,我会保存你的用户名、加密后的密码、注册时间、UA等信息。此类信息不会被公开或与第三方分享。
|
||||
<h2>16. 数据库下载不了呀!</h2>
|
||||
那你试试这几个呢?
|
||||
<a href="/data/yyets_mongo.gz">MongoDB</a> 实时更新,无checksum<br>
|
||||
<a href="/data/yyets_mysql.zip">MySQL 5.7</a> aeee83e54e5de37df9392ab4f01eb9c43437f47d<br>
|
||||
<a href="/data/yyets_sqlite.zip">SQLite</a> 13a4635c6bd41b605a92d33a496bc563f5772652<br>
|
||||
|
||||
<h2>17. 注册需要提供什么?</h2>
|
||||
呃,其实随便写个用户名密码就可以了,没有限制的哦。我使用了 pbkdf2_sha256 安全保存你的密码。
|
||||
我知道这个功能太简陋了,比如说无法找回密码,很多很多。凑合用吧😂
|
||||
哦对了,你的用户信息不会被包含在上述数据库之中。
|
||||
<br>
|
||||
<br>
|
||||
<button><a href="/">返回主页</a></button>
|
||||
|
||||
@@ -101,6 +101,8 @@
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="centered">
|
||||
@@ -142,9 +144,21 @@
|
||||
我的博客 <a class="footer" href="https://dmesg.app/">土豆不好吃</a>
|
||||
</h3>
|
||||
</div>
|
||||
<form method="post" style="display: none" id="login-form">
|
||||
<label>
|
||||
<input type="text" required="required" placeholder="用户名" name="username"/>
|
||||
</label>
|
||||
<label>
|
||||
<input type="password" required="required" placeholder="密码" name="password"/>
|
||||
</label>
|
||||
<button class="but" type="submit" onclick="login()">登录</button>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
<script src="js/axios.min.js"></script>
|
||||
<script src="js/analytics.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
<link href="css/noty.css" rel="stylesheet">
|
||||
<script src="js/noty.min.js"></script>
|
||||
|
||||
<script>
|
||||
accessMetrics("access");
|
||||
@@ -152,9 +166,7 @@
|
||||
// get top
|
||||
axios.get('/api/top')
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
showTop(response.data)
|
||||
// console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
@@ -197,11 +209,52 @@
|
||||
divChild.innerHTML = html;
|
||||
divChild = divChild.firstChild;
|
||||
divParent.appendChild(divChild);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
document.getElementsByClassName("tab")[0].firstElementChild.className += " active"
|
||||
document.getElementById("ALL").style.display = "block"
|
||||
|
||||
let like = document.getElementById("LIKE");
|
||||
let user_cookie = getCookie("username");
|
||||
let login_form = document.getElementById("login-form");
|
||||
if (user_cookie === "") {
|
||||
like.innerHTML = "请登录,未注册用户将会自动注册<br><br>" + login_form.innerHTML + "<br>"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function login() {
|
||||
let username = document.getElementsByName("username")[0].value
|
||||
let password = document.getElementsByName("password")[0].value
|
||||
axios.post('/api/user', {
|
||||
username: username,
|
||||
password: password
|
||||
})
|
||||
.then(function (response) {
|
||||
let message = response.status === 201 ? "注册成功" : "登录成功"
|
||||
new Noty({
|
||||
type: 'success',
|
||||
layout: 'topRight',
|
||||
theme: 'relax',
|
||||
text: message,
|
||||
timeout: 1500
|
||||
}).show();
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 3000);
|
||||
})
|
||||
.catch(function (error) {
|
||||
new Noty({
|
||||
type: 'error',
|
||||
layout: 'topRight',
|
||||
theme: 'relax',
|
||||
text: error.response.data,
|
||||
timeout: 1500
|
||||
}).show();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +269,6 @@
|
||||
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||
}
|
||||
document.getElementById(cityName).style.display = "block";
|
||||
console.log(evt)
|
||||
evt.currentTarget.className += " active";
|
||||
}
|
||||
|
||||
|
||||
9
web/js/analytics.js
vendored
9
web/js/analytics.js
vendored
@@ -1,9 +0,0 @@
|
||||
function accessMetrics(type) {
|
||||
axios.post('/api/metrics?type=' + type)
|
||||
.then(function (response) {
|
||||
// console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// console.log(error);
|
||||
});
|
||||
}
|
||||
21
web/js/common.js
Normal file
21
web/js/common.js
Normal file
@@ -0,0 +1,21 @@
|
||||
function getCookie(cname) {
|
||||
var name = cname + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i].trim();
|
||||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
function accessMetrics(type) {
|
||||
axios.post('/api/metrics?type=' + type)
|
||||
.then(function (response) {
|
||||
// console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
17
web/js/noty.min.js
vendored
Normal file
17
web/js/noty.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
46
web/resource.html
vendored
46
web/resource.html
vendored
@@ -204,7 +204,8 @@
|
||||
<div class="ui-limit">
|
||||
<div class="logo-wrapper">
|
||||
<div class="logo"></div>
|
||||
<div class="desc">资源分享站</div>
|
||||
<div class="desc"><a href="/">资源分享站</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group" role="group" aria-label="...">
|
||||
|
||||
@@ -214,7 +215,7 @@
|
||||
|
||||
<div class="view-count">
|
||||
|
||||
<span class="desc"><i class="fa fa-home"></i><a href="index.html">返回首页</a> </span>
|
||||
<span class="desc"><i class="fa fa-heart"></i><a id="fav" onclick="addFav()">添加收藏</a> </span>
|
||||
<span class="desc"><i class="fa fa-eye"></i>访问量:</span>
|
||||
<span class="count">{{ info.views }}</span>
|
||||
</div>
|
||||
@@ -683,6 +684,10 @@
|
||||
alert("发生错误!请清除浏览器缓存后重试")
|
||||
}
|
||||
var data = json.data;
|
||||
// like button
|
||||
let fav = document.getElementById("fav");
|
||||
fav.innerHTML = json.is_like ? "取消收藏" : "添加收藏"
|
||||
|
||||
document.title = data.info.cnname + " " + data.info.enname;
|
||||
var list = data.list;
|
||||
//me.addSupprtTip(data.ad_rate)
|
||||
@@ -874,8 +879,41 @@
|
||||
|
||||
</script>
|
||||
<script src="js/axios.min.js"></script>
|
||||
<script src="js/analytics.js"></script>
|
||||
<script> accessMetrics("resource");
|
||||
<link href="css/noty.css" rel="stylesheet">
|
||||
<script src="js/noty.min.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
|
||||
<script>
|
||||
accessMetrics("resource");
|
||||
|
||||
function addFav() {
|
||||
let resource_id = document.URL.split("id=")[1];
|
||||
|
||||
axios.patch('/api/user', {
|
||||
resource_id: resource_id,
|
||||
}).then(function (response) {
|
||||
new Noty({
|
||||
type: 'success',
|
||||
layout: 'topRight',
|
||||
theme: 'relax',
|
||||
text: response.data,
|
||||
timeout: 1500
|
||||
}).show();
|
||||
// change text to un fav
|
||||
let fav = document.getElementById("fav")
|
||||
fav.innerHTML = fav.innerHTML === "取消收藏" ? "添加收藏" : "取消收藏"
|
||||
|
||||
|
||||
}).catch(function (error) {
|
||||
new Noty({
|
||||
type: 'error',
|
||||
layout: 'topRight',
|
||||
theme: 'relax',
|
||||
text: "发生错误 " + error.response.data,
|
||||
timeout: 1500
|
||||
}).show();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/axios.min.js"></script>
|
||||
<script src="js/analytics.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
|
||||
<script>
|
||||
let kwe = document.URL.split("kw=")[1];
|
||||
|
||||
114
web/server.py
114
web/server.py
@@ -14,6 +14,8 @@ import json
|
||||
import time
|
||||
from datetime import date, timedelta
|
||||
|
||||
from passlib.hash import pbkdf2_sha256
|
||||
|
||||
import redis
|
||||
import pymongo
|
||||
from http import HTTPStatus
|
||||
@@ -135,6 +137,80 @@ class IndexHandler(BaseHandler):
|
||||
self.write(resp)
|
||||
|
||||
|
||||
class UserHandler(BaseHandler):
|
||||
executor = ThreadPoolExecutor(10)
|
||||
|
||||
def set_login(self, username):
|
||||
self.set_secure_cookie("username", username)
|
||||
|
||||
@run_on_executor()
|
||||
def login_user(self):
|
||||
data = json.loads(self.request.body)
|
||||
username = data["username"]
|
||||
password = data["password"]
|
||||
data = self.mongo.db["users"].find_one({"username": username})
|
||||
returned_value = ""
|
||||
if data:
|
||||
# try to login
|
||||
stored_password = data["password"]
|
||||
if pbkdf2_sha256.verify(password, stored_password):
|
||||
self.set_status(HTTPStatus.OK)
|
||||
self.set_login(username)
|
||||
else:
|
||||
self.set_status(HTTPStatus.FORBIDDEN)
|
||||
returned_value = "用户名或密码错误"
|
||||
else:
|
||||
hash_value = pbkdf2_sha256.hash(password)
|
||||
try:
|
||||
self.mongo.db["users"].insert_one(dict(username=username, password=hash_value,
|
||||
date=time.asctime(),
|
||||
ip=self.request.remote_ip,
|
||||
browser=self.request.headers['user-agent']
|
||||
)
|
||||
)
|
||||
self.set_login(username)
|
||||
self.set_status(HTTPStatus.CREATED)
|
||||
except Exception as e:
|
||||
self.set_status(HTTPStatus.INTERNAL_SERVER_ERROR)
|
||||
returned_value = str(e)
|
||||
|
||||
return returned_value
|
||||
|
||||
@run_on_executor()
|
||||
def add_remove_fav(self):
|
||||
data = json.loads(self.request.body)
|
||||
resource_id = int(data["resource_id"])
|
||||
username = self.get_secure_cookie("username")
|
||||
if username:
|
||||
username = username.decode('u8')
|
||||
like_list: list = self.mongo.db["users"].find_one({"username": username}).get("like", [])
|
||||
if resource_id in like_list:
|
||||
returned_value = "已取消收藏"
|
||||
like_list.remove(resource_id)
|
||||
else:
|
||||
self.set_status(HTTPStatus.CREATED)
|
||||
like_list.append(resource_id)
|
||||
returned_value = "已添加收藏"
|
||||
|
||||
value = dict(like=like_list)
|
||||
self.mongo.db["users"].update_one({"username": username}, {'$set': value})
|
||||
else:
|
||||
returned_value = "请先登录"
|
||||
self.set_status(HTTPStatus.UNAUTHORIZED)
|
||||
|
||||
return returned_value
|
||||
|
||||
@gen.coroutine
|
||||
def post(self):
|
||||
resp = yield self.login_user()
|
||||
self.write(resp)
|
||||
|
||||
@gen.coroutine
|
||||
def patch(self):
|
||||
resp = yield self.add_remove_fav()
|
||||
self.write(resp)
|
||||
|
||||
|
||||
class ResourceHandler(BaseHandler):
|
||||
executor = ThreadPoolExecutor(100)
|
||||
|
||||
@@ -168,7 +244,15 @@ class ResourceHandler(BaseHandler):
|
||||
|
||||
if forbidden:
|
||||
self.set_status(HTTPStatus.FORBIDDEN)
|
||||
|
||||
# is fav?
|
||||
username = self.get_secure_cookie("username")
|
||||
if username:
|
||||
username = username.decode('u8')
|
||||
user_like_data = self.mongo.db["users"].find_one({"username": username})
|
||||
if user_like_data and param in user_like_data.get("like", []):
|
||||
data["is_like"] = True
|
||||
else:
|
||||
data["is_like"] = False
|
||||
return data
|
||||
|
||||
@run_on_executor()
|
||||
@@ -201,21 +285,32 @@ class ResourceHandler(BaseHandler):
|
||||
|
||||
class TopHandler(BaseHandler):
|
||||
executor = ThreadPoolExecutor(100)
|
||||
projection = {'_id': False, 'data.info': True}
|
||||
|
||||
def get_user_like(self) -> list:
|
||||
username = self.get_secure_cookie("username")
|
||||
if username:
|
||||
like_list = self.mongo.db["users"].find_one({"username": username.decode('u8')}).get("like", [])
|
||||
|
||||
data = self.mongo.db["yyets"].find({"data.info.id": {"$in": like_list}}, self.projection) \
|
||||
.sort("data.info.views", pymongo.DESCENDING)
|
||||
return list(data)
|
||||
return []
|
||||
|
||||
@run_on_executor()
|
||||
def get_top_resource(self):
|
||||
projection = {'_id': False,
|
||||
'data.info': True,
|
||||
}
|
||||
|
||||
area_dict = dict(ALL={"$regex": ".*"}, US="美国", JP="日本", KR="韩国", UK="英国")
|
||||
all_data = {}
|
||||
for abbr, area in area_dict.items():
|
||||
data = self.mongo.db["yyets"].find({"data.info.area": area}, projection).sort("data.info.views",
|
||||
pymongo.DESCENDING).limit(15)
|
||||
data = self.mongo.db["yyets"].find({"data.info.area": area}, self.projection). \
|
||||
sort("data.info.views", pymongo.DESCENDING).limit(15)
|
||||
all_data[abbr] = list(data)
|
||||
|
||||
area_dict["ALL"] = "全部"
|
||||
area_dict["LIKE"] = "收藏"
|
||||
all_data["LIKE"] = self.get_user_like()
|
||||
|
||||
all_data["class"] = area_dict
|
||||
return all_data
|
||||
|
||||
@@ -397,6 +492,7 @@ class RunServer:
|
||||
handlers = [
|
||||
(r'/api/resource', ResourceHandler),
|
||||
(r'/api/top', TopHandler),
|
||||
(r'/api/user', UserHandler),
|
||||
(r'/api/name', NameHandler),
|
||||
(r'/api/metrics', MetricsHandler),
|
||||
(r'/api/grafana/', GrafanaIndexHandler),
|
||||
@@ -407,8 +503,12 @@ class RunServer:
|
||||
(r'/(.*\.html|.*\.js|.*\.css|.*\.png|.*\.jpg|.*\.ico|.*\.gif|.*\.woff2|.*\.gz|.*\.zip)', web.StaticFileHandler,
|
||||
{'path': static_path}),
|
||||
]
|
||||
settings = {
|
||||
"cookie_secret": "eo2kcgpKwXj8Q3PKYj6nIL1J4j3b58DX"
|
||||
}
|
||||
|
||||
application = web.Application(handlers, xheaders=True, default_handler_class=NotFoundHandler)
|
||||
application = web.Application(handlers, xheaders=True, default_handler_class=NotFoundHandler,
|
||||
**settings)
|
||||
|
||||
@staticmethod
|
||||
def run_server(port, host, **kwargs):
|
||||
|
||||
Reference in New Issue
Block a user