Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c0b970a95 |
15 changed files with 66 additions and 569 deletions
|
|
@ -1,7 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
(require racket/splicing
|
(require web-server/servlet-dispatch
|
||||||
web-server/servlet-dispatch
|
|
||||||
web-server/safety-limits
|
|
||||||
"src/config.rkt"
|
"src/config.rkt"
|
||||||
"src/dispatcher-tree.rkt"
|
"src/dispatcher-tree.rkt"
|
||||||
"src/reloadable.rkt")
|
"src/reloadable.rkt")
|
||||||
|
|
@ -11,9 +9,6 @@
|
||||||
(reloadable-entry-point->procedure
|
(reloadable-entry-point->procedure
|
||||||
(make-reloadable-entry-point (quote varname) filename))))
|
(make-reloadable-entry-point (quote varname) filename))))
|
||||||
|
|
||||||
(require-reloadable "src/page-captcha.rkt" page-captcha)
|
|
||||||
(require-reloadable "src/page-captcha.rkt" page-captcha-image)
|
|
||||||
(require-reloadable "src/page-captcha.rkt" page-captcha-verify)
|
|
||||||
(require-reloadable "src/page-category.rkt" page-category)
|
(require-reloadable "src/page-category.rkt" page-category)
|
||||||
(require-reloadable "src/page-global-search.rkt" page-global-search)
|
(require-reloadable "src/page-global-search.rkt" page-global-search)
|
||||||
(require-reloadable "src/page-home.rkt" page-home)
|
(require-reloadable "src/page-home.rkt" page-home)
|
||||||
|
|
@ -27,9 +22,7 @@
|
||||||
(require-reloadable "src/page-static-archive.rkt" page-static-archive)
|
(require-reloadable "src/page-static-archive.rkt" page-static-archive)
|
||||||
(require-reloadable "src/page-subdomain.rkt" subdomain-dispatcher)
|
(require-reloadable "src/page-subdomain.rkt" subdomain-dispatcher)
|
||||||
(require-reloadable "src/page-wiki.rkt" page-wiki)
|
(require-reloadable "src/page-wiki.rkt" page-wiki)
|
||||||
(require-reloadable "src/page-wiki.rkt" page-wiki-with-data)
|
|
||||||
(require-reloadable "src/page-wiki-offline.rkt" page-wiki-offline)
|
(require-reloadable "src/page-wiki-offline.rkt" page-wiki-offline)
|
||||||
(require-reloadable "src/page-wiki-jsonp.rkt" page-wiki-jsonp)
|
|
||||||
(require-reloadable "src/page-file.rkt" page-file)
|
(require-reloadable "src/page-file.rkt" page-file)
|
||||||
|
|
||||||
(reload!)
|
(reload!)
|
||||||
|
|
@ -41,14 +34,10 @@
|
||||||
(if (config-true? 'debug) "127.0.0.1" #f)
|
(if (config-true? 'debug) "127.0.0.1" #f)
|
||||||
(config-get 'bind_host))
|
(config-get 'bind_host))
|
||||||
#:port (string->number (config-get 'port))
|
#:port (string->number (config-get 'port))
|
||||||
#:safety-limits (make-safety-limits #:max-request-body-length (* 8 1024 1024))
|
|
||||||
(λ (quit)
|
(λ (quit)
|
||||||
(channel-put ch (lambda () (semaphore-post quit)))
|
(channel-put ch (lambda () (semaphore-post quit)))
|
||||||
(dispatcher-tree
|
(dispatcher-tree
|
||||||
; order of these does not matter
|
; order of these does not matter
|
||||||
page-captcha
|
|
||||||
page-captcha-image
|
|
||||||
page-captcha-verify
|
|
||||||
page-category
|
page-category
|
||||||
page-global-search
|
page-global-search
|
||||||
page-home
|
page-home
|
||||||
|
|
@ -59,9 +48,7 @@
|
||||||
page-set-user-settings
|
page-set-user-settings
|
||||||
page-static-archive
|
page-static-archive
|
||||||
page-wiki
|
page-wiki
|
||||||
page-wiki-with-data
|
|
||||||
page-wiki-offline
|
page-wiki-offline
|
||||||
page-wiki-jsonp
|
|
||||||
page-file
|
page-file
|
||||||
redirect-wiki-home
|
redirect-wiki-home
|
||||||
static-dispatcher
|
static-dispatcher
|
||||||
|
|
|
||||||
9
dist.rkt
9
dist.rkt
|
|
@ -3,7 +3,6 @@
|
||||||
"src/config.rkt"
|
"src/config.rkt"
|
||||||
"src/dispatcher-tree.rkt")
|
"src/dispatcher-tree.rkt")
|
||||||
|
|
||||||
(require (only-in "src/page-captcha.rkt" page-captcha page-captcha-image page-captcha-verify))
|
|
||||||
(require (only-in "src/page-category.rkt" page-category))
|
(require (only-in "src/page-category.rkt" page-category))
|
||||||
(require (only-in "src/page-global-search.rkt" page-global-search))
|
(require (only-in "src/page-global-search.rkt" page-global-search))
|
||||||
(require (only-in "src/page-home.rkt" page-home))
|
(require (only-in "src/page-home.rkt" page-home))
|
||||||
|
|
@ -16,9 +15,8 @@
|
||||||
(require (only-in "src/page-static.rkt" static-dispatcher))
|
(require (only-in "src/page-static.rkt" static-dispatcher))
|
||||||
(require (only-in "src/page-static-archive.rkt" page-static-archive))
|
(require (only-in "src/page-static-archive.rkt" page-static-archive))
|
||||||
(require (only-in "src/page-subdomain.rkt" subdomain-dispatcher))
|
(require (only-in "src/page-subdomain.rkt" subdomain-dispatcher))
|
||||||
(require (only-in "src/page-wiki.rkt" page-wiki page-wiki-with-data))
|
(require (only-in "src/page-wiki.rkt" page-wiki))
|
||||||
(require (only-in "src/page-wiki-offline.rkt" page-wiki-offline))
|
(require (only-in "src/page-wiki-offline.rkt" page-wiki-offline))
|
||||||
(require (only-in "src/page-wiki-jsonp.rkt" page-wiki-jsonp))
|
|
||||||
(require (only-in "src/page-file.rkt" page-file))
|
(require (only-in "src/page-file.rkt" page-file))
|
||||||
|
|
||||||
(serve/launch/wait
|
(serve/launch/wait
|
||||||
|
|
@ -29,9 +27,6 @@
|
||||||
(λ (quit)
|
(λ (quit)
|
||||||
(dispatcher-tree
|
(dispatcher-tree
|
||||||
; order of these does not matter
|
; order of these does not matter
|
||||||
page-captcha
|
|
||||||
page-captcha-image
|
|
||||||
page-captcha-verify
|
|
||||||
page-category
|
page-category
|
||||||
page-global-search
|
page-global-search
|
||||||
page-home
|
page-home
|
||||||
|
|
@ -43,8 +38,6 @@
|
||||||
page-static-archive
|
page-static-archive
|
||||||
page-wiki
|
page-wiki
|
||||||
page-wiki-offline
|
page-wiki-offline
|
||||||
page-wiki-with-data
|
|
||||||
page-wiki-jsonp
|
|
||||||
page-file
|
page-file
|
||||||
redirect-wiki-home
|
redirect-wiki-home
|
||||||
static-dispatcher
|
static-dispatcher
|
||||||
|
|
|
||||||
|
|
@ -58,16 +58,6 @@
|
||||||
(data-src "https://static.wikia.nocookie.net/nice-image-thumbnail.png")
|
(data-src "https://static.wikia.nocookie.net/nice-image-thumbnail.png")
|
||||||
(class "thumbimage")))))
|
(class "thumbimage")))))
|
||||||
(figcaption "Test figure!"))
|
(figcaption "Test figure!"))
|
||||||
(div (@ (type "slideshow") (position "center") (widths "500") (mode "slideshow") (seq-no "0") (id "slideshow-0") (hash "b62d0efee427ad7dff1026e6e9dd078c") (class "wikia-slideshow wikia-gallery slideshow-center"))
|
|
||||||
(div (@ (class "wikia-slideshow-wrapper") (style "width: 500px") (data-test-outer-width))
|
|
||||||
(div (@ (class "wikia-slideshow-images-wrapper"))
|
|
||||||
(ul (@ (class "wikia-slideshow-images neutral") (style "height: 375px; width: 500px") (data-test-inner-width))
|
|
||||||
(li (@ (class "wikia-slideshow-first-image"))
|
|
||||||
(a (@ (class "image lightbox") (title "Expand slideshow") (id "slideshow-0-0") (style "width: 420px"))
|
|
||||||
(img (@ (data-src "https://static.wikia.nocookie.net/example/images/3/3d/Image.jpg/revision/latest/scale-to-width-down/500?cb=20140129105112") (class "thumbimage") (width "500") (height "281") (style "border: 0px;"))))
|
|
||||||
(div (@ (class "wikia-slideshow-overlay"))
|
|
||||||
(div (@ (class "wikia-slideshow-image-caption"))
|
|
||||||
"Example caption")))))))
|
|
||||||
(iframe (@ (src "https://example.com/iframe-src")))
|
(iframe (@ (src "https://example.com/iframe-src")))
|
||||||
(div (@ (class "reviews"))
|
(div (@ (class "reviews"))
|
||||||
(header "GameSpot Expert Reviews"))
|
(header "GameSpot Expert Reviews"))
|
||||||
|
|
|
||||||
|
|
@ -163,13 +163,11 @@
|
||||||
#:head-data [head-data-in #f]
|
#:head-data [head-data-in #f]
|
||||||
#:siteinfo [siteinfo-in #f]
|
#:siteinfo [siteinfo-in #f]
|
||||||
#:user-cookies [user-cookies-in #f]
|
#:user-cookies [user-cookies-in #f]
|
||||||
#:online-styles [online-styles #t]
|
#:online-styles [online-styles #t])
|
||||||
#:path [path-in #f])
|
|
||||||
(define siteinfo (or siteinfo-in siteinfo-default))
|
(define siteinfo (or siteinfo-in siteinfo-default))
|
||||||
(define head-data (or head-data-in ((head-data-getter wikiname))))
|
(define head-data (or head-data-in ((head-data-getter wikiname))))
|
||||||
(define user-cookies (or user-cookies-in (user-cookies-getter req)))
|
(define user-cookies (or user-cookies-in (user-cookies-getter req)))
|
||||||
(define origin (format "https://~a.fandom.com" wikiname))
|
(define origin (format "https://~a.fandom.com" wikiname))
|
||||||
(define path (or path-in ""))
|
|
||||||
(define required-styles
|
(define required-styles
|
||||||
(cond
|
(cond
|
||||||
[online-styles
|
[online-styles
|
||||||
|
|
@ -199,8 +197,7 @@
|
||||||
(link (@ (rel "stylesheet") (type "text/css") (href ,(get-static-url "main.css"))))
|
(link (@ (rel "stylesheet") (type "text/css") (href ,(get-static-url "main.css"))))
|
||||||
(script "const BWData = "
|
(script "const BWData = "
|
||||||
,(jsexpr->string (hasheq 'wikiname wikiname
|
,(jsexpr->string (hasheq 'wikiname wikiname
|
||||||
'strict_proxy (config-true? 'strict_proxy)
|
'strict_proxy (config-true? 'strict_proxy))))
|
||||||
'path path)))
|
|
||||||
,(if (config-true? 'feature_search_suggestions)
|
,(if (config-true? 'feature_search_suggestions)
|
||||||
`(script (@ (type "module") (src ,(get-static-url "search-suggestions.js"))))
|
`(script (@ (type "module") (src ,(get-static-url "search-suggestions.js"))))
|
||||||
"")
|
"")
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,6 @@
|
||||||
(feature_offline::only . "false")
|
(feature_offline::only . "false")
|
||||||
(feature_offline::search . "fandom")
|
(feature_offline::search . "fandom")
|
||||||
|
|
||||||
(feature_jsonp::enabled . "true")
|
|
||||||
|
|
||||||
(captcha::enabled . "false")
|
|
||||||
(captcha::log . "false")
|
|
||||||
(captcha::ip_header . "")
|
|
||||||
|
|
||||||
(access_log::enabled . "false")
|
(access_log::enabled . "false")
|
||||||
|
|
||||||
(promotions::indie_wiki_buddy . "banner home")))
|
(promotions::indie_wiki_buddy . "banner home")))
|
||||||
|
|
|
||||||
12
src/data.rkt
12
src/data.rkt
|
|
@ -20,7 +20,6 @@
|
||||||
(struct-out license^)
|
(struct-out license^)
|
||||||
(struct-out head-data^)
|
(struct-out head-data^)
|
||||||
(struct-out user-cookies^)
|
(struct-out user-cookies^)
|
||||||
data->siteinfo
|
|
||||||
siteinfo-fetch
|
siteinfo-fetch
|
||||||
siteinfo-default
|
siteinfo-default
|
||||||
license-default
|
license-default
|
||||||
|
|
@ -67,15 +66,12 @@
|
||||||
("formatversion" . "2"))))
|
("formatversion" . "2"))))
|
||||||
(cond [(= (easy:response-status-code res) 200)
|
(cond [(= (easy:response-status-code res) 200)
|
||||||
(define data (easy:response-json res))
|
(define data (easy:response-json res))
|
||||||
(data->siteinfo data)]
|
(siteinfo^ (jp "/query/general/sitename" data)
|
||||||
|
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
||||||
|
(license^ (jp "/query/rightsinfo/text" data)
|
||||||
|
(jp "/query/rightsinfo/url" data)))]
|
||||||
[else siteinfo-default])]))
|
[else siteinfo-default])]))
|
||||||
|
|
||||||
(define (data->siteinfo data)
|
|
||||||
(siteinfo^ (jp "/query/general/sitename" data)
|
|
||||||
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
|
||||||
(license^ (jp "/query/rightsinfo/text" data)
|
|
||||||
(jp "/query/rightsinfo/url" data))))
|
|
||||||
|
|
||||||
(define/memoize (head-data-getter wikiname) #:hash hash
|
(define/memoize (head-data-getter wikiname) #:hash hash
|
||||||
;; data will be stored here, can be referenced by the memoized closure
|
;; data will be stored here, can be referenced by the memoized closure
|
||||||
(define this-data head-data-default)
|
(define this-data head-data-default)
|
||||||
|
|
|
||||||
|
|
@ -56,25 +56,15 @@
|
||||||
(sequencer:make
|
(sequencer:make
|
||||||
subdomain-dispatcher
|
subdomain-dispatcher
|
||||||
(pathprocedure:make "/" (page ds page-home))
|
(pathprocedure:make "/" (page ds page-home))
|
||||||
(filter:make #rx"^/static/" (hash-ref ds 'static-dispatcher))
|
|
||||||
(filter:make (pregexp "^/captcha/img/[0-9]+/[0-9]+$") (lift:make (page ds page-captcha-image)))
|
|
||||||
(filter:make (pregexp "^/captcha/verify/[0-9]+/[0-9]+/[0-9]+/[0-9]+$") (lift:make (page ds page-captcha-verify)))
|
|
||||||
(if (config-true? 'captcha::enabled)
|
|
||||||
(lift:make (page ds page-captcha))
|
|
||||||
(λ (_conn _req) (next-dispatcher)))
|
|
||||||
(pathprocedure:make "/proxy" (page ds page-proxy))
|
(pathprocedure:make "/proxy" (page ds page-proxy))
|
||||||
(pathprocedure:make "/search" (page ds page-global-search))
|
(pathprocedure:make "/search" (page ds page-global-search))
|
||||||
(pathprocedure:make "/set-user-settings" (page ds page-set-user-settings))
|
(pathprocedure:make "/set-user-settings" (page ds page-set-user-settings))
|
||||||
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (page ds page-it-works))
|
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (page ds page-it-works))
|
||||||
(pathprocedure:make "/api/render/wiki" (page ds page-wiki-with-data))
|
|
||||||
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (page ds page-category)))
|
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (page ds page-category)))
|
||||||
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (page ds page-file)))
|
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (page ds page-file)))
|
||||||
(if (config-true? 'feature_offline::enabled)
|
(if (config-true? 'feature_offline::enabled)
|
||||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki-offline)))
|
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki-offline)))
|
||||||
(λ (_conn _req) (next-dispatcher)))
|
(λ (_conn _req) (next-dispatcher)))
|
||||||
(if (config-true? 'feature_jsonp::enabled)
|
|
||||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki-jsonp)))
|
|
||||||
(λ (_conn _req) (next-dispatcher)))
|
|
||||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki)))
|
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki)))
|
||||||
(filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (page ds page-search)))
|
(filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (page ds page-search)))
|
||||||
(filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (page ds redirect-wiki-home)))
|
(filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (page ds redirect-wiki-home)))
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
#lang typed/racket/base
|
#lang typed/racket/base
|
||||||
(require racket/format
|
(require "config.rkt"
|
||||||
racket/string
|
|
||||||
"config.rkt"
|
|
||||||
"../lib/url-utils.rkt")
|
"../lib/url-utils.rkt")
|
||||||
(define-type Headers (HashTable Symbol (U Bytes String)))
|
(define-type Headers (HashTable Symbol (U Bytes String)))
|
||||||
(require/typed net/http-easy
|
(require/typed net/http-easy
|
||||||
[#:opaque Timeout-Config timeout-config?]
|
[#:opaque Timeout-Config timeout-config?]
|
||||||
[#:opaque Response response?]
|
[#:opaque Response response?]
|
||||||
[#:opaque Session session?]
|
[#:opaque Session session?]
|
||||||
[response-status-code (Response -> Natural)]
|
|
||||||
[current-session (Parameter Session)]
|
[current-session (Parameter Session)]
|
||||||
[current-user-agent (Parameter (U Bytes String))]
|
|
||||||
[make-timeout-config ([#:lease Positive-Real] [#:connect Positive-Real] -> Timeout-Config)]
|
[make-timeout-config ([#:lease Positive-Real] [#:connect Positive-Real] -> Timeout-Config)]
|
||||||
[get ((U Bytes String)
|
[get ((U Bytes String)
|
||||||
[#:close? Boolean]
|
[#:close? Boolean]
|
||||||
|
|
@ -26,41 +22,19 @@
|
||||||
fandom-get-api
|
fandom-get-api
|
||||||
timeouts)
|
timeouts)
|
||||||
|
|
||||||
(unless (string-contains? (~a (current-user-agent)) "BreezeWiki")
|
|
||||||
(current-user-agent
|
|
||||||
(format "BreezeWiki/1.0 (~a) ~a"
|
|
||||||
(if (config-true? 'canonical_origin)
|
|
||||||
(config-get 'canonical_origin)
|
|
||||||
"local")
|
|
||||||
(current-user-agent))))
|
|
||||||
|
|
||||||
(define timeouts (make-timeout-config #:lease 5 #:connect 5))
|
(define timeouts (make-timeout-config #:lease 5 #:connect 5))
|
||||||
|
|
||||||
(: last-failure Flonum)
|
|
||||||
(define last-failure 0.0)
|
|
||||||
(: stored-failure (Option Response))
|
|
||||||
(define stored-failure #f)
|
|
||||||
(define failure-persist-time 30000)
|
|
||||||
|
|
||||||
(: no-headers Headers)
|
(: no-headers Headers)
|
||||||
(define no-headers '#hasheq())
|
(define no-headers '#hasheq())
|
||||||
|
|
||||||
(: fandom-get (String String [#:headers (Option Headers)] -> Response))
|
(: fandom-get (String String [#:headers (Option Headers)] -> Response))
|
||||||
(define (fandom-get wikiname path #:headers [headers #f])
|
(define (fandom-get wikiname path #:headers [headers #f])
|
||||||
(or
|
(define dest-url (string-append "https://www.fandom.com" path))
|
||||||
(and ((current-inexact-milliseconds) . < . (+ last-failure failure-persist-time)) stored-failure)
|
(define host (string-append wikiname ".fandom.com"))
|
||||||
(let ()
|
(log-outgoing wikiname path)
|
||||||
(define dest-url (string-append "https://www.fandom.com" path))
|
(get dest-url
|
||||||
(define host (string-append wikiname ".fandom.com"))
|
#:timeouts timeouts
|
||||||
(log-outgoing wikiname path)
|
#:headers (hash-set (or headers no-headers) 'Host host)))
|
||||||
(define res
|
|
||||||
(get dest-url
|
|
||||||
#:timeouts timeouts
|
|
||||||
#:headers (hash-set (or headers no-headers) 'Host host)))
|
|
||||||
(when (memq (response-status-code res) '(403 406))
|
|
||||||
(set! last-failure (current-inexact-milliseconds))
|
|
||||||
(set! stored-failure res))
|
|
||||||
res)))
|
|
||||||
|
|
||||||
(: fandom-get-api (String (Listof (Pair String String)) [#:headers (Option Headers)] -> Response))
|
(: fandom-get-api (String (Listof (Pair String String)) [#:headers (Option Headers)] -> Response))
|
||||||
(define (fandom-get-api wikiname params #:headers [headers #f])
|
(define (fandom-get-api wikiname params #:headers [headers #f])
|
||||||
|
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
#lang racket/base
|
|
||||||
(require racket/class
|
|
||||||
racket/dict
|
|
||||||
racket/draw
|
|
||||||
pict
|
|
||||||
file/convertible
|
|
||||||
racket/format
|
|
||||||
racket/list
|
|
||||||
racket/math
|
|
||||||
racket/match
|
|
||||||
web-server/http
|
|
||||||
(only-in web-server/dispatchers/dispatch next-dispatcher)
|
|
||||||
net/url
|
|
||||||
(only-in net/cookies/server cookie->set-cookie-header cookie-header->alist)
|
|
||||||
html-writing
|
|
||||||
"application-globals.rkt"
|
|
||||||
"data.rkt"
|
|
||||||
"config.rkt"
|
|
||||||
"static-data.rkt"
|
|
||||||
"../lib/url-utils.rkt"
|
|
||||||
"../lib/xexpr-utils.rkt")
|
|
||||||
|
|
||||||
(provide
|
|
||||||
page-captcha
|
|
||||||
page-captcha-image
|
|
||||||
page-captcha-verify)
|
|
||||||
|
|
||||||
(define (get-ip req)
|
|
||||||
(define header
|
|
||||||
(if (config-true? 'captcha::ip_header)
|
|
||||||
(headers-assq* (string->bytes/utf-8 (config-get 'captcha::ip_header)) (request-headers/raw req))
|
|
||||||
#f))
|
|
||||||
(if header
|
|
||||||
(~a (header-value header))
|
|
||||||
(request-client-ip req)))
|
|
||||||
|
|
||||||
(define (get-rng req)
|
|
||||||
(parameterize ([current-pseudo-random-generator (make-pseudo-random-generator)])
|
|
||||||
(define ip-segments (regexp-match* "[0-9]+" (get-ip req)))
|
|
||||||
(define seed
|
|
||||||
(modulo
|
|
||||||
(for/sum ([i (in-naturals)]
|
|
||||||
[s ip-segments])
|
|
||||||
(* (add1 i) (add1 (string->number s))))
|
|
||||||
(expt 2 32)))
|
|
||||||
(random-seed seed)
|
|
||||||
(current-pseudo-random-generator)))
|
|
||||||
|
|
||||||
(define (get-key-solution req)
|
|
||||||
(parameterize ([current-pseudo-random-generator (get-rng req)])
|
|
||||||
(random 1 (add1 9))))
|
|
||||||
|
|
||||||
(define diameter 35)
|
|
||||||
(define font (make-object font% 12 'system))
|
|
||||||
(define msg "I'm not a robot!")
|
|
||||||
(define checkbox (filled-ellipse diameter diameter #:color "Pale Goldenrod"))
|
|
||||||
(define assembly
|
|
||||||
(frame
|
|
||||||
(inset
|
|
||||||
(hc-append
|
|
||||||
8
|
|
||||||
checkbox
|
|
||||||
(text msg font))
|
|
||||||
8)))
|
|
||||||
(define-values (inner-x inner-y) (cc-find assembly checkbox))
|
|
||||||
(define-values (lt-x lt-y) (lt-find assembly checkbox))
|
|
||||||
(define-values (rb-x rb-y) (rb-find assembly checkbox))
|
|
||||||
|
|
||||||
(define (get-coordinate-solution req w h)
|
|
||||||
(parameterize ([current-pseudo-random-generator (get-rng req)])
|
|
||||||
(values (random (exact-truncate lt-x) (exact-truncate (- w rb-x)))
|
|
||||||
(random (exact-truncate lt-y) (exact-truncate (- h rb-y))))))
|
|
||||||
|
|
||||||
(define (page-captcha req)
|
|
||||||
(define cookie-header (headers-assq* #"cookie" (request-headers/raw req)))
|
|
||||||
(define cookies-alist (if cookie-header (cookie-header->alist (header-value cookie-header) bytes->string/utf-8) null))
|
|
||||||
(for ([pair cookies-alist])
|
|
||||||
(match pair
|
|
||||||
[(cons "captcha" method)
|
|
||||||
(when (config-true? 'captcha::log)
|
|
||||||
(printf "captcha skip - via ~a [~a] - ~v~n" method (get-ip req) (url->string (request-uri req))))
|
|
||||||
(next-dispatcher)]
|
|
||||||
[_ (void)]))
|
|
||||||
(response-handler
|
|
||||||
(define body
|
|
||||||
`(*TOP*
|
|
||||||
(*DECL* DOCTYPE html)
|
|
||||||
(html
|
|
||||||
(head
|
|
||||||
(meta (@ (name "viewport") (content "width=device-width, initial-scale=1")))
|
|
||||||
(title "Checking you're not a bot...")
|
|
||||||
(link (@ (rel "stylesheet") (type "text/css") (href ,(get-static-url "internal.css"))))
|
|
||||||
(link (@ (rel "stylesheet") (type "text/css") (href ,(get-static-url "main.css"))))
|
|
||||||
(link (@ (rel "icon") (href ,(head-data^-icon-url head-data-default))))
|
|
||||||
(script (@ (defer) (src ,(get-static-url "captcha.js"))))
|
|
||||||
(body (@ (class "skin-fandomdesktop theme-fandomdesktop-light internal"))
|
|
||||||
(div (@ (class "main-container"))
|
|
||||||
(div (@ (class "fandom-community-header__background tileBoth header")))
|
|
||||||
(div (@ (class "page"))
|
|
||||||
(main (@ (class "page__main"))
|
|
||||||
(div (@ (class "custom-top"))
|
|
||||||
(h1 (@ (class "page-title"))
|
|
||||||
"Checking you're not a bot..."))
|
|
||||||
(div (@ (id "content") #;(class "page-content"))
|
|
||||||
(div (@ (id "mw-content-text"))
|
|
||||||
(p "To confirm, please click directly in the circle, or hold down the " ,(~a (get-key-solution req)) " key on your keyboard.")
|
|
||||||
(noscript (p "JavaScript is required for the captcha. Sorry!"))
|
|
||||||
(div (@ (id "captcha-area")))))
|
|
||||||
,(application-footer #f)))))))))
|
|
||||||
(when (config-true? 'debug)
|
|
||||||
(xexp->html body))
|
|
||||||
(response/output
|
|
||||||
#:code 200
|
|
||||||
#:headers always-headers
|
|
||||||
(λ (out)
|
|
||||||
(write-html body out)))))
|
|
||||||
|
|
||||||
(define (page-captcha-image req)
|
|
||||||
(response-handler
|
|
||||||
(define w (string->number (path/param-path (third (url-path (request-uri req))))))
|
|
||||||
(define h (string->number (path/param-path (fourth (url-path (request-uri req))))))
|
|
||||||
(define-values (at-x at-y) (get-coordinate-solution req w h))
|
|
||||||
(when (config-true? 'captcha::log)
|
|
||||||
(printf "captcha show - size ~a x ~a - solution ~a x ~a [~a]~n" w h at-x at-y (get-ip req)))
|
|
||||||
#;(printf "target: ~a x ~a~ncanvas: ~a x ~a~npict size: ~a-~a ~a-~a~n" at-x at-y x y lt-x rb-x lt-y rb-y)
|
|
||||||
(define dc (make-object bitmap-dc% #f))
|
|
||||||
(send dc set-font font)
|
|
||||||
(define bm (make-object bitmap% w h #f #f))
|
|
||||||
(send dc set-bitmap bm)
|
|
||||||
(draw-pict
|
|
||||||
assembly
|
|
||||||
dc
|
|
||||||
(- at-x inner-x)
|
|
||||||
(- at-y inner-y))
|
|
||||||
(define image (convert bm 'png-bytes))
|
|
||||||
(response/output
|
|
||||||
#:mime-type #"image/png"
|
|
||||||
#:headers (list (header #"Cache-Control" #"no-cache"))
|
|
||||||
(λ (out) (display image out)))))
|
|
||||||
|
|
||||||
(define (page-captcha-verify req)
|
|
||||||
(response-handler
|
|
||||||
(match-define (list w h x y) (for/list ([segment (drop (url-path (request-uri req)) 2)])
|
|
||||||
(string->number (path/param-path segment))))
|
|
||||||
#;(printf "solution: ~a x ~a~ncoordinate: ~a x ~a~ndist^2: ~a~n" solution-x solution-y x y dist)
|
|
||||||
(define headers
|
|
||||||
(build-headers
|
|
||||||
always-headers
|
|
||||||
(cond
|
|
||||||
[(and (= y 0) (= x (get-key-solution req)))
|
|
||||||
(when (config-true? 'captcha::log)
|
|
||||||
(printf "captcha pass - key ~a [~a]~n" x (get-ip req)))
|
|
||||||
|
|
||||||
(header #"Set-Cookie" (cookie->set-cookie-header (make-cookie "captcha" "key" #:path "/" #:max-age (* 60 60 24 365 10))))]
|
|
||||||
[(= y 0)
|
|
||||||
(when (config-true? 'captcha::log)
|
|
||||||
(printf "captcha fail - key ~a instead of ~a [~a]~n" x (get-key-solution req) (get-ip req)))]
|
|
||||||
[else (void)])
|
|
||||||
(when (> y 0)
|
|
||||||
(let-values ([(solution-x solution-y) (get-coordinate-solution req w h)])
|
|
||||||
(let ([dist (+ (expt (- x solution-x) 2) (expt (- y solution-y) 2))])
|
|
||||||
(cond
|
|
||||||
[(dist . < . (expt (/ diameter 2) 2))
|
|
||||||
(when (config-true? 'captcha::log)
|
|
||||||
(printf "captcha pass - coordinate, dist^2 ~a [~a]~n" dist (get-ip req)))
|
|
||||||
(header #"Set-Cookie" (cookie->set-cookie-header (make-cookie "captcha" "coordinate" #:path "/" #:max-age (* 60 60 24 365 10))))]
|
|
||||||
[else
|
|
||||||
(when (config-true? 'captcha::log)
|
|
||||||
(printf "captcha pass - coordinate, dist^2 ~a [~a]~n" dist (get-ip req)))]))))))
|
|
||||||
(match (dict-ref (url-query (request-uri req)) 'from #f)
|
|
||||||
[(? string? dest)
|
|
||||||
(response/output
|
|
||||||
#:code 302
|
|
||||||
#:mime-type #"text/plain"
|
|
||||||
#:headers (cons (header #"Location" (string->bytes/utf-8 dest)) headers)
|
|
||||||
(λ (out)
|
|
||||||
(displayln "Checking your answer..." out)))]
|
|
||||||
[#f (next-dispatcher)])))
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
#lang racket/base
|
|
||||||
(require racket/list
|
|
||||||
racket/string
|
|
||||||
web-server/http
|
|
||||||
net/url-structs
|
|
||||||
html-writing
|
|
||||||
"application-globals.rkt"
|
|
||||||
"data.rkt"
|
|
||||||
"config.rkt"
|
|
||||||
"../lib/url-utils.rkt"
|
|
||||||
"../lib/xexpr-utils.rkt"
|
|
||||||
"../lib/archive-file-mappings.rkt"
|
|
||||||
"static-data.rkt")
|
|
||||||
|
|
||||||
(provide
|
|
||||||
page-wiki-jsonp)
|
|
||||||
|
|
||||||
(define (page-wiki-jsonp req)
|
|
||||||
(response-handler
|
|
||||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
|
||||||
(define segments (map path/param-path (cdr (url-path (request-uri req)))))
|
|
||||||
(define path (string-join (cdr segments) "/"))
|
|
||||||
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname path))
|
|
||||||
|
|
||||||
(define wiki-page-script-url
|
|
||||||
(format "https://~a.fandom.com/api.php?~a"
|
|
||||||
wikiname
|
|
||||||
(params->query `(("action" . "parse")
|
|
||||||
("page" . ,path)
|
|
||||||
("prop" . "text|headhtml|langlinks")
|
|
||||||
("formatversion" . "2")
|
|
||||||
("format" . "json")
|
|
||||||
("callback" . "proxy.wikipage")))))
|
|
||||||
(define siteinfo-script-url
|
|
||||||
(format "https://~a.fandom.com/api.php?~a"
|
|
||||||
wikiname
|
|
||||||
(params->query `(("action" . "query")
|
|
||||||
("meta" . "siteinfo")
|
|
||||||
("siprop" . "general|rightsinfo")
|
|
||||||
("format" . "json")
|
|
||||||
("formatversion" . "2")
|
|
||||||
("callback" . "proxy.siteinfo")))))
|
|
||||||
|
|
||||||
(define body
|
|
||||||
(generate-wiki-page
|
|
||||||
`(div
|
|
||||||
(noscript "You have to enable JavaScript to load wiki pages. Sorry!")
|
|
||||||
(div (@ (id "loading")))
|
|
||||||
(div (@ (id "progress-bar") (style "margin-bottom: 50vh"))
|
|
||||||
(progress))
|
|
||||||
(script #<<END
|
|
||||||
var jsonpData = {}
|
|
||||||
var proxy = new Proxy(jsonpData, {get(obj, prop) { return value => obj[prop] = value }})
|
|
||||||
END
|
|
||||||
)
|
|
||||||
(script (@ (async) (src ,wiki-page-script-url)))
|
|
||||||
(script (@ (async) (src ,siteinfo-script-url)))
|
|
||||||
(script (@ (type "module") (src ,(get-static-url "jsonp.js")))))
|
|
||||||
#:req req
|
|
||||||
#:source-url source-url
|
|
||||||
#:wikiname wikiname
|
|
||||||
#:title (url-segments->guess-title segments)
|
|
||||||
#:siteinfo siteinfo-default
|
|
||||||
#:path path))
|
|
||||||
(when (config-true? 'debug)
|
|
||||||
(xexp->html body))
|
|
||||||
(response/output
|
|
||||||
#:code 200
|
|
||||||
#:headers always-headers
|
|
||||||
(λ (out)
|
|
||||||
(write-html body out)))))
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
racket/string
|
racket/string
|
||||||
; libs
|
; libs
|
||||||
(prefix-in easy: net/http-easy)
|
(prefix-in easy: net/http-easy)
|
||||||
json
|
|
||||||
; html libs
|
; html libs
|
||||||
"../lib/html-parsing/main.rkt"
|
"../lib/html-parsing/main.rkt"
|
||||||
html-writing
|
html-writing
|
||||||
|
|
@ -19,7 +18,8 @@
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"fandom-request.rkt"
|
"fandom-request.rkt"
|
||||||
"../lib/archive-file-mappings.rkt"
|
"../lib/pure-utils.rkt"
|
||||||
|
"../lib/syntax.rkt"
|
||||||
"../lib/thread-utils.rkt"
|
"../lib/thread-utils.rkt"
|
||||||
"../lib/tree-updater.rkt"
|
"../lib/tree-updater.rkt"
|
||||||
"../lib/url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
(provide
|
(provide
|
||||||
; used by the web server
|
; used by the web server
|
||||||
page-wiki
|
page-wiki
|
||||||
page-wiki-with-data
|
|
||||||
; used by page-category, and similar pages that are partially wiki pages
|
; used by page-category, and similar pages that are partially wiki pages
|
||||||
update-tree-wiki
|
update-tree-wiki
|
||||||
preprocess-html-wiki)
|
preprocess-html-wiki)
|
||||||
|
|
@ -38,9 +37,8 @@
|
||||||
|
|
||||||
(define (page-wiki req)
|
(define (page-wiki req)
|
||||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||||
(define segments (map path/param-path (cdr (url-path (request-uri req)))))
|
|
||||||
(define user-cookies (user-cookies-getter req))
|
(define user-cookies (user-cookies-getter req))
|
||||||
(define path (string-join (cdr segments) "/"))
|
(define path (string-join (map path/param-path (cddr (url-path (request-uri req)))) "/"))
|
||||||
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname path))
|
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname path))
|
||||||
|
|
||||||
(define-values (dest-res siteinfo)
|
(define-values (dest-res siteinfo)
|
||||||
|
|
@ -59,111 +57,54 @@
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
[(eq? 200 (easy:response-status-code dest-res))
|
[(eq? 200 (easy:response-status-code dest-res))
|
||||||
(let ([data (easy:response-json dest-res)])
|
(let* ([data (easy:response-json dest-res)]
|
||||||
|
[title (jp "/parse/title" data "")]
|
||||||
|
[page-html (jp "/parse/text" data "")]
|
||||||
|
[page-html (preprocess-html-wiki page-html)]
|
||||||
|
[page (html->xexp page-html)]
|
||||||
|
[head-data ((head-data-getter wikiname) data)])
|
||||||
(if (equal? "missingtitle" (jp "/error/code" data #f))
|
(if (equal? "missingtitle" (jp "/error/code" data #f))
|
||||||
(next-dispatcher)
|
(next-dispatcher)
|
||||||
(take-json-rewrite-and-return-page data)))]
|
(response-handler
|
||||||
|
(define body
|
||||||
|
(generate-wiki-page
|
||||||
|
(update-tree-wiki page wikiname)
|
||||||
|
#:req req
|
||||||
|
#:source-url source-url
|
||||||
|
#:wikiname wikiname
|
||||||
|
#:title title
|
||||||
|
#:head-data head-data
|
||||||
|
#:siteinfo siteinfo))
|
||||||
|
(define redirect-query-parameter (dict-ref (url-query (request-uri req)) 'redirect "yes"))
|
||||||
|
(define redirect-msg ((query-selector (attribute-selector 'class "redirectMsg") body)))
|
||||||
|
(define redirect-msg-a (if redirect-msg
|
||||||
|
((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))
|
||||||
|
#f))
|
||||||
|
(define headers
|
||||||
|
(build-headers
|
||||||
|
always-headers
|
||||||
|
; redirect-query-parameter: only the string "no" is significant:
|
||||||
|
; https://github.com/Wikia/app/blob/fe60579a53f16816d65dad1644363160a63206a6/includes/Wiki.php#L367
|
||||||
|
(when (and redirect-msg-a
|
||||||
|
(not (equal? redirect-query-parameter "no")))
|
||||||
|
(let* ([dest (get-attribute 'href (bits->attributes redirect-msg-a))]
|
||||||
|
[value (bytes-append #"0;url=" (string->bytes/utf-8 dest))])
|
||||||
|
(header #"Refresh" value)))))
|
||||||
|
(when (config-true? 'debug)
|
||||||
|
; used for its side effects
|
||||||
|
; convert to string with error checking, error will be raised if xexp is invalid
|
||||||
|
(xexp->html body))
|
||||||
|
(response/output
|
||||||
|
#:code 200
|
||||||
|
#:headers headers
|
||||||
|
(λ (out)
|
||||||
|
(write-html body out))))))]
|
||||||
[(eq? 404 (easy:response-status-code dest-res))
|
[(eq? 404 (easy:response-status-code dest-res))
|
||||||
(next-dispatcher)]
|
(next-dispatcher)]
|
||||||
[(memq (easy:response-status-code dest-res) '(403 406))
|
|
||||||
(response-handler
|
|
||||||
(define body
|
|
||||||
(generate-wiki-page
|
|
||||||
`(div
|
|
||||||
(p "Sorry! Fandom isn't allowing BreezeWiki to show pages right now.")
|
|
||||||
(p "We'll automatically try again in 30 seconds, so please stay on this page and be patient.")
|
|
||||||
(p (small "In a hurry? " (a (@ (href ,source-url)) "Click here to read the page on Fandom."))))
|
|
||||||
#:req req
|
|
||||||
#:source-url source-url
|
|
||||||
#:wikiname wikiname
|
|
||||||
#:title (url-segments->guess-title segments)
|
|
||||||
#:siteinfo siteinfo))
|
|
||||||
(response/output
|
|
||||||
#:code 503
|
|
||||||
#:headers (build-headers
|
|
||||||
always-headers
|
|
||||||
(header #"Retry-After" #"30")
|
|
||||||
(header #"Cache-Control" #"max-age=30, public")
|
|
||||||
(header #"Refresh" #"35"))
|
|
||||||
(λ (out)
|
|
||||||
(write-html body out))))]
|
|
||||||
[else
|
[else
|
||||||
(response-handler
|
(response-handler
|
||||||
(error 'page-wiki "Tried to load page ~a/~a~nSadly, the page didn't load because Fandom returned status code ~a with response:~n~a"
|
(error 'page-wiki "Tried to load page ~a/~v~nSadly, the page didn't load because Fandom returned status code ~a with response:~n~a"
|
||||||
wikiname
|
wikiname
|
||||||
path
|
path
|
||||||
(easy:response-status-code dest-res)
|
(easy:response-status-code dest-res)
|
||||||
(easy:response-body dest-res)))]))
|
(easy:response-body dest-res)))]))
|
||||||
|
|
||||||
(define (page-wiki-with-data req)
|
|
||||||
(response-handler
|
|
||||||
(let/cc return
|
|
||||||
(define post-data/bytes (request-post-data/raw req))
|
|
||||||
(when (not post-data/bytes)
|
|
||||||
(return (response/jsexpr
|
|
||||||
#:code 400
|
|
||||||
#:headers always-headers
|
|
||||||
'#hasheq((error .
|
|
||||||
#hasheq((code . "breezewiki")
|
|
||||||
(info . "POST requests only, please.")))))))
|
|
||||||
|
|
||||||
(define origin-header
|
|
||||||
(or (headers-assq* #"origin" (request-headers/raw req))
|
|
||||||
(headers-assq* #"referer" (request-headers/raw req))))
|
|
||||||
(when (or (not origin-header) (not (string-prefix? (bytes->string/latin-1 (header-value origin-header)) (config-get 'canonical_origin))))
|
|
||||||
(return (response/jsexpr
|
|
||||||
#:code 400
|
|
||||||
#:headers always-headers
|
|
||||||
'#hasheq((error .
|
|
||||||
#hasheq((code . "breezewiki")
|
|
||||||
(info . "Origin/Referer header failed validation - cross-origin requests are not allowed here")))))))
|
|
||||||
|
|
||||||
(define post-data/string (bytes->string/utf-8 post-data/bytes))
|
|
||||||
(define post-data (string->jsexpr post-data/string))
|
|
||||||
(define wikiname (jp "/wikiname" post-data))
|
|
||||||
(define path (jp "/path" post-data))
|
|
||||||
(take-json-rewrite-and-return-page
|
|
||||||
#:req req
|
|
||||||
#:wikiname wikiname
|
|
||||||
#:source-url (format "https://~a.fandom.com/wiki/~a" wikiname path)
|
|
||||||
#:data (jp "/data" post-data)
|
|
||||||
#:siteinfo (data->siteinfo (jp "/siteinfo" post-data))))))
|
|
||||||
|
|
||||||
(define (take-json-rewrite-and-return-page #:req req #:wikiname wikiname #:source-url source-url #:data data #:siteinfo siteinfo)
|
|
||||||
(define title (jp "/parse/title" data ""))
|
|
||||||
(define page-html (preprocess-html-wiki (jp "/parse/text" data "")))
|
|
||||||
(define page (html->xexp page-html))
|
|
||||||
(define head-data ((head-data-getter wikiname) data))
|
|
||||||
(response-handler
|
|
||||||
(define body
|
|
||||||
(generate-wiki-page
|
|
||||||
(update-tree-wiki page wikiname)
|
|
||||||
#:req req
|
|
||||||
#:source-url source-url
|
|
||||||
#:wikiname wikiname
|
|
||||||
#:title title
|
|
||||||
#:head-data head-data
|
|
||||||
#:siteinfo siteinfo))
|
|
||||||
(define redirect-query-parameter (dict-ref (url-query (request-uri req)) 'redirect "yes"))
|
|
||||||
(define redirect-msg ((query-selector (attribute-selector 'class "redirectMsg") body)))
|
|
||||||
(define redirect-msg-a (if redirect-msg
|
|
||||||
((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))
|
|
||||||
#f))
|
|
||||||
(define html (xexp->html-bytes body))
|
|
||||||
(define headers
|
|
||||||
(build-headers
|
|
||||||
always-headers
|
|
||||||
; redirect-query-parameter: only the string "no" is significant:
|
|
||||||
; https://github.com/Wikia/app/blob/fe60579a53f16816d65dad1644363160a63206a6/includes/Wiki.php#L367
|
|
||||||
(when (and redirect-msg-a
|
|
||||||
(not (equal? redirect-query-parameter "no")))
|
|
||||||
(let* ([dest (get-attribute 'href (bits->attributes redirect-msg-a))]
|
|
||||||
[value (bytes-append #"0;url=" (string->bytes/utf-8 dest))])
|
|
||||||
(header #"Refresh" value)))))
|
|
||||||
(response/full
|
|
||||||
200
|
|
||||||
#"OK"
|
|
||||||
(current-seconds)
|
|
||||||
#"text/html; charset=utf-8"
|
|
||||||
headers
|
|
||||||
(list html))))
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
const u = new URL(location)
|
|
||||||
const from = u.searchParams.get("from") || location.href
|
|
||||||
let answered = false
|
|
||||||
|
|
||||||
const area = document.getElementById("captcha-area")
|
|
||||||
const areaBox = area.getBoundingClientRect()
|
|
||||||
const width = Math.floor(areaBox.width)
|
|
||||||
const height = Math.floor(window.innerHeight - areaBox.bottom - areaBox.left)
|
|
||||||
const img = document.createElement("img")
|
|
||||||
img.src = `/captcha/img/${width}/${height}`
|
|
||||||
img.addEventListener("click", event => {
|
|
||||||
if (answered) return
|
|
||||||
answered = true
|
|
||||||
location = `/captcha/verify/${width}/${height}/${event.offsetX}/${event.offsetY}?` + new URLSearchParams({from})
|
|
||||||
})
|
|
||||||
area.appendChild(img)
|
|
||||||
|
|
||||||
document.addEventListener("keydown", event => {
|
|
||||||
if (event.repeat) {
|
|
||||||
if (answered) return
|
|
||||||
answered = true
|
|
||||||
location = `/captcha/verify/0/0/${event.key}/0?` + new URLSearchParams({from})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
import {h, htm, render, signal, computed, effect} from "./preact.js"
|
|
||||||
const html = htm.bind(h)
|
|
||||||
|
|
||||||
// *** Loading indicator
|
|
||||||
|
|
||||||
render(html`Loading, please wait...`, document.getElementById("loading"))
|
|
||||||
|
|
||||||
// *** Progress bar
|
|
||||||
|
|
||||||
const progress = signal(null)
|
|
||||||
|
|
||||||
const progressBar = document.getElementById("progress-bar")
|
|
||||||
while (progressBar.childNodes[0] !== undefined) progressBar.childNodes[0].remove() // clear out loading indicators
|
|
||||||
|
|
||||||
render(html`<progress value="${progress}" max="1"></progress>`, progressBar)
|
|
||||||
|
|
||||||
// *** Incoming data processing
|
|
||||||
|
|
||||||
// Handle case where data is immediately available
|
|
||||||
cont()
|
|
||||||
|
|
||||||
// Handle case where data may become available in the future
|
|
||||||
window.proxy = new Proxy(jsonpData, {
|
|
||||||
get(obj, prop) {
|
|
||||||
return value => {
|
|
||||||
obj[prop] = value
|
|
||||||
cont()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// *** Data upload and download
|
|
||||||
|
|
||||||
async function cont() {
|
|
||||||
if (jsonpData.wikipage?.error) return error(jsonpData.wikipage)
|
|
||||||
if (jsonpData.siteinfo?.error) return error(jsonpData.siteinfo)
|
|
||||||
if (!(jsonpData.wikipage && jsonpData.siteinfo)) return
|
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
const uploadFraction = 0.7
|
|
||||||
|
|
||||||
// Upload progress
|
|
||||||
xhr.upload.addEventListener("progress", event => {
|
|
||||||
if (event.lengthComputable) {
|
|
||||||
progress.value = (event.loaded / event.total) * uploadFraction
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Download progress
|
|
||||||
xhr.addEventListener("progress", event => {
|
|
||||||
if (event.lengthComputable) {
|
|
||||||
progress.value = (event.loaded / event.total) * (1 - uploadFraction) + uploadFraction
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
xhr.addEventListener("load", () => {
|
|
||||||
console.log(xhr)
|
|
||||||
const imported = document.importNode(xhr.responseXML.getElementById("content"), true)
|
|
||||||
document.getElementById("content").replaceWith(imported)
|
|
||||||
document.title = xhr.responseXML.title
|
|
||||||
for (const e of xhr.responseXML.head.children) {
|
|
||||||
if (["LINK"].includes(e.tagName)) {
|
|
||||||
const imported = document.importNode(e, true)
|
|
||||||
document.head.appendChild(imported)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const redirectTo = document.querySelector("#content .redirectMsg a")
|
|
||||||
if (redirectTo) {
|
|
||||||
redirectTo.click()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
xhr.open("POST", "/api/render/wiki")
|
|
||||||
xhr.responseType = "document"
|
|
||||||
xhr.send(JSON.stringify({
|
|
||||||
data: jsonpData.wikipage,
|
|
||||||
siteinfo: jsonpData.siteinfo,
|
|
||||||
wikiname: BWData.wikiname,
|
|
||||||
path: BWData.path
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function error(data) {
|
|
||||||
const eContent = document.getElementById("content")
|
|
||||||
while (eContent.childNodes[0] !== undefined) eContent.childNodes[0].remove() // clear out loading indicators
|
|
||||||
document.title = `Error | BreezeWiki`
|
|
||||||
render(html`
|
|
||||||
${data.error.code === "missingtitle"
|
|
||||||
? html`<p><strong>This page doesn't exist on Fandom.</strong></p>`
|
|
||||||
: html`
|
|
||||||
<p>BreezeWiki wasn't able to load this page.</p>
|
|
||||||
<p><strong>${data.error.code}: ${data.error.info}</strong></p>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
<p><small><a href="/${BWData.wikiname}/wiki/Main_Page">Return to the homepage?</a></small></p>
|
|
||||||
`, eContent)
|
|
||||||
}
|
|
||||||
|
|
@ -218,6 +218,13 @@ a.ext-audiobutton { /* see hearthstone/wiki/Diablo_(Duels_hero) */
|
||||||
display: inline !important;
|
display: inline !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* hide slideshows since they're broken
|
||||||
|
* example: ben10/wiki/Celestialsapien_(Classic)#Powers_and_Abilities
|
||||||
|
*/
|
||||||
|
.wikia-slideshow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* animated slots */
|
/* animated slots */
|
||||||
#mw-content-text .animated > :not(.animated-active), #mw-content-text .animated > .animated-subframe > :not(.animated-active) {
|
#mw-content-text .animated > :not(.animated-active), #mw-content-text .animated > .animated-subframe > :not(.animated-active) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,3 @@ User-Agent: *
|
||||||
Disallow: /*/wiki/*
|
Disallow: /*/wiki/*
|
||||||
Disallow: /proxy
|
Disallow: /proxy
|
||||||
Disallow: /set-user-settings
|
Disallow: /set-user-settings
|
||||||
Disallow: /captcha
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue