File size: 1,609 Bytes
0d19174
 
 
 
17c92f2
 
0d19174
17c92f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const express = require('express');
const fetch = require('node-fetch');
const cheerio = require('cheerio');
const { URL } = require('url');

const app = express();
const PORT = 3000;

function absolutize(base, relative) {
  try {
    return new URL(relative, base).toString();
  } catch {
    return relative;
  }
}

app.get('/proxy', async (req, res) => {
  const targetUrl = req.query.url;
  if (!targetUrl) {
    return res.status(400).send('Missing ?url= parameter');
  }

  try {
    const response = await fetch(targetUrl);
    const contentType = response.headers.get('content-type') || '';

    if (contentType.includes('text/html')) {
      const html = await response.text();
      const $ = cheerio.load(html);
      const base = targetUrl;

      // 書き換える属性
      const attributes = ['href', 'src', 'action'];

      $('*').each((i, el) => {
        attributes.forEach(attr => {
          const val = $(el).attr(attr);
          if (val && !val.startsWith('data:') && !val.startsWith('javascript:')) {
            const abs = absolutize(base, val);
            $(el).attr(attr, `/proxy?url=${encodeURIComponent(abs)}`);
          }
        });
      });

      res.set('Content-Type', 'text/html');
      res.send($.html());
    } else {
      // HTML以外はそのままパイプ
      res.set('Content-Type', contentType);
      response.body.pipe(res);
    }
  } catch (err) {
    console.error(err);
    res.status(500).send('プロキシエラー: ' + err.message);
  }
});

app.listen(PORT, () => {
  console.log(`🌐 Proxy server running at http://localhost:${PORT}`);
});