# 【复刻】在 Fly.io 上搭建 Alist

Last updated on March 19, 2024 pm

按 @moeyy 的教程《使用 Fly.io 部署 Alist》完成后，还有一些优化体验的小细节，在此记录一下。

## 获取管理员账号

    1
    2

    flyctl ssh console # 如果失败，打开所部署的应用页面，刷新后多尝试几次
    ./alist admin random

## 更改静态资源地址

### Cloudflare 反代 jsDelivr

1. 新建一个Worker

    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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127

    // 替换成你想镜像的站点
    const upstream = 'cdn.jsdelivr.net'

    // 如果那个站点有专门的移动适配站点，否则保持和上面一致
    const upstream_mobile = 'cdn.jsdelivr.net'

    const blocked_region = []

    const blocked_ip_address = ['0.0.0.0', '127.0.0.1']

    const replace_dict = {
        '$upstream': 'jscdn.limour.top',
        '//cdn.jsdelivr.net': '//jscdn.limour.top'
    }

    //以下内容都不用动
    addEventListener('fetch', event => {
        event.respondWith(fetchAndApply(event.request));
    })

    async function fetchAndApply(request) {

        const region = request.headers.get('cf-ipcountry').toUpperCase();
        const ip_address = request.headers.get('cf-connecting-ip');
        const user_agent = request.headers.get('user-agent');

        let response = null;
        let url = new URL(request.url);
        let url_host = url.host;

        if (url.protocol == 'http:') {
            url.protocol = 'https:'
            response = Response.redirect(url.href);
            return response;
        }

        if (await device_status(user_agent)) {
            upstream_domain = upstream
        } else {
            upstream_domain = upstream_mobile
        }

        url.host = upstream_domain;

        if (blocked_region.includes(region)) {
            response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
                status: 403
            });
        } else if(blocked_ip_address.includes(ip_address)){
            response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
                status: 403
            });
        } else{
            let method = request.method;
            let request_headers = request.headers;
            let new_request_headers = new Headers(request_headers);

            new_request_headers.set('Host', upstream_domain);
            new_request_headers.set('Referer', url.href);

            let original_response = await fetch(url.href, {
                method: method,
                headers: new_request_headers
            })

            let original_response_clone = original_response.clone();
            let original_text = null;
            let response_headers = original_response.headers;
            let new_response_headers = new Headers(response_headers);
            let status = original_response.status;

            new_response_headers.set('access-control-allow-origin', '*');
            new_response_headers.set('access-control-allow-credentials', true);
            new_response_headers.delete('content-security-policy');
            new_response_headers.delete('content-security-policy-report-only');
            new_response_headers.delete('clear-site-data');

            const content_type = new_response_headers.get('content-type');
            if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
                original_text = await replace_response_text(original_response_clone, upstream_domain, url_host);
            } else {
                original_text = original_response_clone.body
            }

            response = new Response(original_text, {
                status,
                headers: new_response_headers
            })
        }
        return response;
    }

    async function replace_response_text(response, upstream_domain, host_name) {
        let text = await response.text()

        var i, j;
        for (i in replace_dict) {
            j = replace_dict[i]
            if (i == '$upstream') {
                i = upstream_domain
            } else if (i == '$custom_domain') {
                i = host_name
            }

            if (j == '$upstream') {
                j = upstream_domain
            } else if (j == '$custom_domain') {
                j = host_name
            }

            let re = new RegExp(i, 'g')
            text = text.replace(re, j);
        }
        return text;
    }

    async function device_status (user_agent_info) {
        var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
        var flag = true;
        for (var v = 0; v < agents.length; v++) {
            if (user_agent_info.indexOf(agents[v]) > 0) {
                flag = false;
                break;
            }
        }
        return flag;
    }

2. 触发器-路由设置为 `jscdn.limour.top/*`

### 转换规则设置允许跨域

### 替换CDN

在AList 管理页面中，将所有的 `cdn.jsdelivr.net` 修改为自己反代的地址

## 添加评论系统

1. 在 Vercel 上部署 Waline
2. AList 管理-设置-全局-自定义头部 中引入 Waline 样式

    1

    <link rel="stylesheet" href="https://unpkg.com/@waline/client@v2/dist/waline.css" />

3. AList 管理-元信息 中引入 Waline 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    #  <center> - 评论 Comments -
    <div id="waline"></div>
    <script type="module">
        import { init } from 'https://unpkg.com/@waline/client@v2/dist/waline.mjs';
        init({
          el: '#waline',
          serverURL: 'https://comments.limour.top',
          pageSize: 3,
        });
    </script>

### Waline 邮件异步推送

Waline 邮件异步推送 by 星辰日记

- 搭建反代服务

    1
    2
    3
    4
    5
    6
    7
    8

    mkdir -p ~/app/mailpush && cd ~/app/mailpush && nano docker-compose.yml
    git clone --depth=1 https://github.com/soxft/waline-async-mail.git
    cd waline-async-mail
    nano config.example.yaml # 修改成自己的邮件服务
    nano templates/guest.html && nano templates/owner.html # 修改成自己的邮件模板
    docker build -t xsot/waline-async-mail . && docker image prune
    cd ~/app/mailpush
    sudo docker-compose up -d

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    version: "3"
    services:
      waline-async-mail:
        image: xsot/waline-async-mail:latest
        volumes:
          - ./waline-async-mail/config.example.yaml:/app/config.yaml
        restart: always

    networks:
      default:
        external: true
        name: ngpm

- 在环境变量中添加 `WEBHOOK`, 值为 `https://api.limour.top/mail/limour`
- 在 Deployments 里，重新部署
- WEBHOOK说明

### 附加 Waline 换 MongoDB

1. MongoDB 官网 新建免费的 M0 数据库，地区选新加坡，放行 `0.0.0.0/0`
2. Vercel 上的 Waline 项目，Settings/Functions 里更改 Region 到新加坡
3. MongoDB 连接里记录下连接信息，选项如下
4. 选择 Drivers 作为连接方式，Driver 为 Node.js，Version为 `2.2.12 or later`
5. 进入 Waline 管理后台，导出数据
6. Vercel 里删除 LEAN 相关的环境变量，按 说明 添加 MongoDB 的连接信息
7. Vercel/Deployments 里选择合适的分支，点旁边的三个点，选择 Redeploy
8. 进入 Waline 管理后台，重新注册后，导入数据

## 自定义域名

- DNS 解析只能是<仅 DNS>
- Domain ownership verification 是 CNAME

## 演示地址

- https://od.limour.top

#alist
【复刻】在 Fly.io 上搭建 Alist
https://hexo.limour.top/-fu-ke--zai-Flyio-shang-da-jian-Alist
Author
Limour
Posted on
August 27, 2023
Updated on
March 19, 2024
Licensed under
【复刻】在 Koyeb 上搭建 RSSHub Previous
【复刻】在 replit 上搭建 WebProxy Next

#### Search

Keyword