Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revert: revert "refactor!: migrate to cloudflare pages" #57

Merged
merged 1 commit into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

Pixiv Service Proxy

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FFreeNowOrg%2FPixivNow&demo-title=PixivNow)

免费部署一个一模一样的服务步骤很简单——点上面的按钮然后一直下一步即可。<br>
各位爹不要再爬我的服务了好吗?100GB/h的额度都能被您爬完了,我自己还怎么看色图呢?<br>
好自为之喵,不要怪我骂人喵。

Expand Down Expand Up @@ -40,7 +43,7 @@ Pixiv Service Proxy

本站的图片使用 CloudFlare Workers 进行代理,可以直接访问欣赏众多插画。

但是由于遭遇了大量不明流量,因此我们暂时开启了图片代理服务的防盗链。
但是由于遭遇了大量不明流量,因此我们暂时开启了图片代理服务的防盗链。如果您有自行部署整站的需要,可以修改 `vercel.json` 中对于图片的重定向配置,图片的请求路径与源站完全一致。

---

Expand Down
82 changes: 49 additions & 33 deletions functions/modules/http.ts → api/http.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { createFexios } from 'fexios'
import type { VercelRequest, VercelResponse } from '@vercel/node'
import axios from 'axios'
import colors from 'picocolors'
import { EventContext, PagesFunction } from '@cloudflare/workers-types'
import escapeRegExp from 'lodash.escaperegexp'

interface Env {}

const PROD = process.env.NODE_ENV === 'production'

export class CookieUtils {
Expand All @@ -18,52 +16,50 @@ export class CookieUtils {
}
}

export async function handleHttpRequest(
ctx: EventContext<any, any, any>
): Promise<Response> {
const { request, params } = ctx
const url = new URL(request.url)
console.info('REQUEST', url.href, params)
export default async function (req: VercelRequest, res: VercelResponse) {
if (!isAccepted(req)) {
return res.status(403).send('403')
}

try {
let { prefix, path } = params
if (Array.isArray(prefix)) prefix = prefix.join('/')
if (Array.isArray(path)) path = path.join('/')
const uri = `/${prefix}/${encodeURI(path)}`.replace(/\/+/g, '/')
const { data } = await ajax.request(uri, {
method: (request.method as any) ?? 'GET',
query: Object.fromEntries(url.searchParams.entries()) ?? {},
body: request.body || undefined,
headers: Object.fromEntries(request.headers.entries()),
const { __PREFIX, __PATH } = req.query
const { data } = await ajax({
method: req.method ?? 'GET',
url: `/${encodeURI(`${__PREFIX}${__PATH ? '/' + __PATH : ''}`)}`,
params: req.query ?? {},
data: req.body || undefined,
headers: req.headers as Record<string, string>,
})
return new Response(data)
res.status(200).send(data)
} catch (e: any) {
console.error('PROXY ERROR', e, e.context)
return new Response(e?.context?.data || e, {
status: e?.response?.status || 500,
})
res.status(e?.response?.status || 500).send(e?.response?.data || e)
}
}

export const ajax = createFexios({
export const ajax = axios.create({
baseURL: 'https://www.pixiv.net/',
query: {},
params: {},
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69',
},
timeout: 30 * 1000,
timeout: 9 * 1000,
})
ajax.on('beforeInit', (ctx) => {
ctx.headers = (ctx.headers || {}) as Record<string, string>
ajax.interceptors.request.use((ctx) => {
// 去除内部参数
ctx.params = ctx.params || {}
delete ctx.params.__PATH
delete ctx.params.__PREFIX

const cookies = CookieUtils.toJSON(ctx.headers.cookie || '')
const csrfToken = ctx.headers['x-csrf-token'] ?? cookies.CSRFTOKEN ?? ''
// 强制覆写部分 headers
ctx.headers = ctx.headers || {}
ctx.headers.host = 'www.pixiv.net'
ctx.headers.origin = 'https://www.pixiv.net'
ctx.headers.referer = 'https://www.pixiv.net/'
ctx.headers['user-agent'] =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62'
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69'
ctx.headers['accept-language'] ??=
'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6'
csrfToken && (ctx.headers['x-csrf-token'] = csrfToken)
Expand All @@ -74,7 +70,7 @@ ajax.on('beforeInit', (ctx) => {
colors.cyan(ctx.url || '')
)
console.info({
query: ctx.query,
params: ctx.params,
data: ctx.data,
cookies,
})
Expand All @@ -92,7 +88,7 @@ ajax.interceptors.response.use((ctx) => {
: ctx.data.toString().trim()
console.info(
colors.green('[SEND] >'),
colors.cyan(ctx.rawRequest?.url?.replace('https://www.pixiv.net', '')),
colors.cyan(ctx.request?.path?.replace('https://www.pixiv.net', '')),
`\n${colors.yellow(typeof ctx.data)} ${
out.length >= 200 ? out.slice(0, 200).trim() + '\n...' : out
}`
Expand Down Expand Up @@ -124,3 +120,23 @@ export function replaceUrlInObject(
}
return obj
}

function isAccepted(req: VercelRequest) {
const { UA_BLACKLIST = '[]' } = process.env
try {
const list: string[] = JSON.parse(UA_BLACKLIST)
const ua = req.headers['user-agent'] ?? ''
return (
!!ua &&
Array.isArray(list) &&
(list.length > 0
? !new RegExp(
`(${list.map((str) => escapeRegExp(str)).join('|')})`,
'gi'
).test(ua)
: true)
)
} catch (e) {
return false
}
}
40 changes: 40 additions & 0 deletions api/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { VercelRequest, VercelResponse } from '@vercel/node'
import axios from 'axios'

export default async (req: VercelRequest, res: VercelResponse) => {
const { __PREFIX, __PATH } = req.query
if (!__PREFIX || !__PATH) {
return res.status(400).send({ message: 'Missing param(s)' })
}

switch (__PREFIX) {
case '~': {
return axios
.get<ArrayBuffer>(`https://s.pximg.net/${__PATH}`, {
responseType: 'arraybuffer',
headers: {
referer: 'https://www.pixiv.net/',
'user-agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0',
},
})
.then(
({ data, headers }) => {
res.setHeader('Content-Type', headers['content-type'])
res.setHeader(
'Cache-Control',
`public, max-age=${12 * 60 * 60 * 3600}`
)
res.status(200).send(Buffer.from(data))
},
(err) => {
return res
.status(err?.response?.status || 500)
.send(err?.response?.data || err)
}
)
}
default:
return res.status(400).send({ message: 'Invalid request' })
}
}
38 changes: 17 additions & 21 deletions functions/api/random.ts → api/random.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
import { VercelRequest, VercelResponse } from '@vercel/node'
import { formatInTimeZone } from 'date-fns-tz'
import { ajax } from '../modules/http.js'
import { Artwork } from '../../src/types/Artworks.js'
import { EventContext } from '@cloudflare/workers-types'
import { ajax } from './http.js'
import { Artwork } from '../src/types/Artworks.js'

type ArtworkOrAd = Artwork | { isAdContainer: boolean }

export async function onRequestGet(
ctx: EventContext<any, any, any>
): Promise<Response> {
const req = ctx.request
const url = new URL(req.url)

export default async (req: VercelRequest, res: VercelResponse) => {
const requestImage =
(req.headers.get('accept')?.includes('image') ||
url.searchParams.get('format') === 'image') &&
url.searchParams.get('format') !== 'json'

(req.headers.accept?.includes('image') || req.query.format === 'image') &&
req.query.format !== 'json'
try {
const data: { illusts?: ArtworkOrAd[] } = (
await ajax.get('/ajax/illust/discovery', {
query: {
mode: url.searchParams.get('mode') ?? 'safe',
max: requestImage ? '1' : url.searchParams.get('max') ?? '18',
await ajax({
url: '/ajax/illust/discovery',
params: {
mode: req.query.mode ?? 'safe',
max: requestImage ? '1' : req.query.max ?? '18',
},
headers: Object.fromEntries(req.headers.entries()),
headers: req.headers,
})
).data
const illusts = (data.illusts ?? []).filter((value): value is Artwork =>
Expand All @@ -44,11 +38,13 @@ export async function onRequestGet(
}
})
if (requestImage) {
return Response.redirect(illusts[0].urls.original, 302)
res.redirect(illusts[0].urls.regular)
return
} else {
return Response.json(illusts)
res.send(illusts)
return
}
} catch (e: any) {
return new Response('error', { status: 500 })
res.status(e?.response?.status ?? 500).send(e?.response?.data ?? e)
}
}
2 changes: 1 addition & 1 deletion functions/api/user.ts1 → api/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { VercelRequest, VercelResponse } from '@vercel/node'
import { load } from 'cheerio'
import { ajax, replaceUrlInObject } from '../modules/http.js'
import { ajax, replaceUrlInObject } from './http.js'

export default async (req: VercelRequest, res: VercelResponse) => {
const token = req.cookies.PHPSESSID || req.query.token
Expand Down
4 changes: 1 addition & 3 deletions auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ declare global {
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const injectLocal: typeof import('@vueuse/core')['injectLocal']
const isDefined: typeof import('@vueuse/core')['isDefined']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
Expand Down Expand Up @@ -68,7 +67,6 @@ declare global {
const onUpdated: typeof import('vue')['onUpdated']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const provide: typeof import('vue')['provide']
const provideLocal: typeof import('@vueuse/core')['provideLocal']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: typeof import('vue')['reactive']
Expand Down Expand Up @@ -288,5 +286,5 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
}
11 changes: 0 additions & 11 deletions functions/ajax/[[path]].ts

This file was deleted.

28 changes: 0 additions & 28 deletions functions/modules/image.ts

This file was deleted.

11 changes: 0 additions & 11 deletions functions/~/[[path]].ts

This file was deleted.

27 changes: 12 additions & 15 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PixivNow</title>
</head>
<body>
<noscript
>This site requires JavaScript enabled. Please check your browser
settings.</noscript
>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PixivNow</title>
</head>
<body>
<noscript>This site requires JavaScript enabled. Please check your browser settings.</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Loading
Loading