用 ServBay+Python 复刻“死了么”

0 阅读5分钟

最近 “死了么” 打卡 App 火遍朋友圈,大家都在晒 “我还在” 的状态。

image.png

咱们搞技术的可不止看热闹,今天就用 ServBay 搭配 Python,3 分钟手搓一款同款功能的 “生存打卡神器”,全程无繁琐配置,新手也能轻松拿捏。

image.png

下面是详细操作步骤,跟着做就能快速上线属于你的专属打卡工具。

第一步:打开 ServBay 软件,在左侧导航栏找到[软件包] -「Python」选项卡,无需提前下载 Python 安装包,直接在版本列表中找到「Python 3.11」,点击右侧[下载]按钮,全程仅需几秒钟即可完成安装,自动适配系统环境,不用手动配置环境变量。

image.png

安装完成后,找到 ServBay 安装 Python 3.11 的路径;

打开系统自带「终端」,输入命令创建名为「sileme_env」的虚拟环境:python3 -m venv sileme_env

激活虚拟环境activate后进入独立虚拟环境cd sileme_env\Scripts。

保持虚拟环境激活状态,在终端中输入命令:pip install flask,按下回车后等待几秒,即可完成 Flask 框架安装,运行环境至此搭建完毕。

第二步:打开PyCharm,找到我们新建的「sileme_env」文件夹,添加3.11的解释器,创建一个名为「app.py」的文件(作为 Python 入口文件),复制代码粘贴进去,代码自带数据库初始化、打卡提交、记录查询功能,无需额外配置数据库,启动成功后终端会显示127.0.0.1:5000,保持终端窗口运行不要关闭:

from flask import Flask, render_template, request, redirect, url_for

import sqlite3

from datetime import datetime

import os

import random

 

# Initialize Flask application

app = Flask(__name__)

# Database file path (stored in project directory, automatically recognized by ServBay)

DB_PATH = os.path.join(app.root_path, 'sileme.db')

 

# Random check-in funny phrases list (can add/modify as needed)

random_text = [

    "Didn't get pissed off by bugs today",

    "Successfully wrote 100 lines of code",

    "Slacked off without getting caught",

    "ServBay environment is super stable",

    "Didn't lose more hair today",

    "Coffee kept me alive"

]

 

# Initialize SQLite database and create check-in record table

def init_db():

    conn = sqlite3.connect(DB_PATH)

    cursor = conn.cursor()

    cursor.execute('''

        CREATE TABLE IF NOT EXISTS checkin (

            id INTEGER PRIMARY KEY AUTOINCREMENT,

            username TEXT NOT NULL,

            checkin_time DATETIME NOT NULL,

            status TEXT NOT NULL

        )

    ''')

    conn.commit()

    conn.close()

 

# Automatically initialize database on first run

init_db()

 

# Home page: check-in form + historical records display

@app.route('/', methods=['GET', 'POST'])

def index():

    if request.method == 'POST':

        # Get username from user input, default to Anonymous User

        username = request.form.get('username', 'Anonymous User')

        # Record current check-in time

        checkin_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        # Randomly get a funny phrase as check-in status

        status = random.choice(random_text)

        # Insert data into database

        conn = sqlite3.connect(DB_PATH)

        cursor = conn.cursor()

        cursor.execute(

            'INSERT INTO checkin (username, checkin_time, status) VALUES (?, ?, ?)',

            (username, checkin_time, status)

        )

        conn.commit()

        conn.close()

        # Refresh page after successful check-in

        return redirect(url_for('index'))

    

    # Get all check-in records, display in reverse chronological order

    conn = sqlite3.connect(DB_PATH)

    cursor = conn.cursor()

    cursor.execute('SELECT username, checkin_time, status FROM checkin ORDER BY checkin_time DESC')

    records = cursor.fetchall()

    conn.close()

    

    # Render front-end page and pass check-in records

    return render_template('index.html', records=records)

 

# Adapt to ServBay running configuration

if __name__ == '__main__':

    app.run(debug=True, host='0.0.0.0')

在「sileme_env」文件夹内,新建一个名为「templates」的子文件夹(Flask 默认读取该文件夹下的 HTML 文件),进入「templates」文件夹后,新建「index.html」文件,复制粘贴代码打造前端界面,样式简洁直观,支持打卡与记录查看:

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Work Survival Check-in Tool</title>

    <style>

        * {

            margin: 0;

            padding: 0;

            box-sizing: border-box;

            font-family: "Segoe UI", Arial, sans-serif;

        }

        body {

            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);

            min-height: 100vh;

            padding: 20px;

            display: flex;

            align-items: center;

            justify-content: center;

        }

        .container {

            max-width: 800px;

            width: 100%;

            background: white;

            padding: 40px;

            border-radius: 16px;

            box-shadow: 0 8px 32px rgba(0,0,0,0.1);

            border: 1px solid rgba(255,255,255,0.8);

        }

        h1 {

            text-align: center;

            margin-bottom: 30px;

            color: #2d3748;

            font-weight: 600;

            font-size: 28px;

            position: relative;

            padding-bottom: 15px;

        }

        h1::after {

            content: '';

            position: absolute;

            bottom: 0;

            left: 50%;

            transform: translateX(-50%);

            width: 80px;

            height: 3px;

            background-color: #2f54eb;

            border-radius: 3px;

        }

        .checkin-form {

            text-align: center;

            margin-bottom: 40px;

            padding: 20px;

            background-color: #f8f9ff;

            border-radius: 12px;

        }

        .checkin-form input {

            padding: 14px 20px;

            width: 100%;

            max-width: 350px;

            border: 1px solid #e2e8f0;

            border-radius: 8px;

            font-size: 16px;

            transition: all 0.3s;

            margin-bottom: 15px;

        }

        .checkin-form input:focus {

            outline: none;

            border-color: #2f54eb;

            box-shadow: 0 0 0 3px rgba(47, 84, 235, 0.1);

        }

        .checkin-form button {

            padding: 14px 32px;

            background-color: #2f54eb;

            color: white;

            border: none;

            border-radius: 8px;

            font-size: 16px;

            font-weight: 500;

            cursor: pointer;

            transition: all 0.3s;

            margin-left: 0;

        }

        .checkin-form button:hover {

            background-color: #1d39c4;

            transform: translateY(-2px);

            box-shadow: 0 4px 12px rgba(47, 84, 235, 0.2);

        }

        .records h3 {

            color: #2d3748;

            margin-bottom: 20px;

            border-left: 4px solid #2f54eb;

            padding-left: 15px;

            font-weight: 600;

            font-size: 20px;

        }

        .record-list {

            max-height: 400px;

            overflow-y: auto;

            padding-right: 10px;

        }

        .record-item {

            padding: 18px;

            border-bottom: 1px solid #f0f0f0;

            font-size: 15px;

            color: #4a5568;

            border-radius: 8px;

            margin-bottom: 10px;

            background-color: #fafafa;

            transition: background-color 0.2s;

        }

        .record-item:hover {

            background-color: #f5f5f5;

        }

        .record-item:last-child {

            border-bottom: none;

            margin-bottom: 0;

        }

        .empty-record {

            text-align: center;

            padding: 40px 20px;

            color: #718096;

            font-style: italic;

        }

 

        /* Responsive adaptation - mobile optimization */

        @media (max-width: 600px) {

            .container {

                padding: 20px;

            }

            h1 {

                font-size: 24px;

            }

            .checkin-form {

                padding: 15px;

            }

            .record-item {

                padding: 15px;

            }

        }

    </style>

</head>

<body>

    <div class="container">

        <h1>Work Survival Check-in Tool</h1>

        <div class="checkin-form">

            <form method="POST">

                <input type="text" name="username" placeholder="Enter your name" required>

                <button type="submit">Check-in to prove I'm alive</button>

            </form>

        </div>

        <div class="records">

            <h3>Check-in Records</h3>

            <div class="record-list">

                {% if records %}

                    {% for record in records %}

                    <div class="record-item">

                        <strong>{{ record[0] }}</strong> at {{ record[1] }} said: {{ record[2] }}

                    </div>

                    {% endfor %}

                {% else %}

                    <div class="empty-record">No one has checked in yet, be the first to prove you're here!</div>

                {% endif %}

            </div>

        </div>

    </div>

</body>

</html>

第三步:回到 ServBay 软件,点击左侧「Web服务器」选项卡,点击右上角「新建服务器」按钮,弹出配置窗口后,按以下参数填写:

网站名称:Survival Check-in Tool;

输入本地域名sileme.test(无需备案,本地可直接访问,自定义即可);

网站类型:选择「Reverse Proxy」;

IP Address为127.0.0.1;

Port为5000;

其余选项保持默认,无需额外设置。

所有参数填写完成后,点击「保存」按钮,ServBay 会自动完成反向代理配置。

image.png

配置完成后,点击右侧「访问网站」按钮,自动打开浏览器,此时你的专属打卡应用就正式上线了。

image.png

整个过程全程依赖 ServBay,不用手动配置环境变量、Nginx、数据库,也不用担心端口冲突、版本兼容问题,让你专注于功能开发,3 分钟就能复刻爆款打卡工具。