2023年12月

Vely 可让你在网络应用程序中利用 C 语言的强大功能。

Vely 将 C 语言的高性能和低内存占用与 PHP 等语言的易用性和安全性相结合。作为自由开源软件,它以 GPLv3 和 LGPL 3 授权,所以你甚至可以用它来构建商业软件。

利用 Vely 构建 SaaS

你可以使用 Vely 创建一个多租户网络应用程序,它可以作为软件即服务模式(SaaS)在互联网上运行。每个用户都有一个完全独立的数据空间。

在这个网络应用程序示例中,用户可以注册一个笔记本服务来创建笔记,然后查看和删除它们。它仅用了 7 个源文件,310 行代码,就展示了如何集成多项技术:

  • MariaDB
  • 网络浏览器
  • Apache
  • Unix 套接字

运作原理

以下是从用户的角度来看应用程序是如何工作的。下图是代码演示。

该应用允许用户通过指定电子邮件地址和密码创建新的登录名。你可以用任何你喜欢的方式设置它们,例如运用 CSS:

创建一个用户账户

验证用户的电子邮件:

验证用户的电子邮件地址

每个用户使用自己独有的用户名和密码登录:

用户登录

一旦登录,用户就可以添加笔记:

用户可以添加笔记

用户可以获取笔记列表:

用户列举笔记

删除笔记之前,应用会申请确认信息:

删除笔记之前,应用会申请确认信息

用户确认后,笔记被删除:

用户确认后,笔记被删除

设置先决条件

遵照 Vely.dev 上的安装指示。这是一个使用 DNF、APT、Pacman 或者 Zypper 等标准工具包的快速流程。

由于它们都是这个范例的一部分,你必须安装 Apache 作为网络服务器,安装 MariaDB 作为数据库。

安装 Vely 后,如使用 Vim,打开里面的“语法高亮显示”:

vv -m

获取源代码

这个演示 SaaS 应用程序的源代码是 Vely 安装的一部分。为每个应用程序创建一个单独的源代码目录不失为一个好主意(而且你可以按自己喜好命名)。在这种情况下,解包源代码会帮你完成这些工作:

$ tar xvf $(vv -o)/examples/multitenant_SaaS.tar.gz
$ cd multitenant_SaaS

默认情况下,该应用程序以 multitenant_SaaS 命名,但你可以将其命名为任何内容(如果这么做,其他每个地方你都需要改一下)。

创建应用程序

第一步是创建一个应用程序。使用 Vely 的 vf 工具就可以轻松完成:

$ sudo vf -i-u $(whoami) multitenant_SaaS

这个命令创建了一个新的应用程序主目录(/var/lib/vv/multitenant_SaaS),并帮你执行应用程序设置。通常,这意味着在该主目录中创建各种子目录并分配权限。在这种情况下,只有当前用户(whoami 的结果)拥有目录,具有 0700 权限,这确保了其他人没有访问文件的权限。

创建数据库

在你键入任何代码之前,你需要一个能够存储该应用程序所用信息的空间。首先,创建一个名为 db_multitenant_SaaS 的 MariaDB 数据库,由用户名为 vely 的用户所有,密码为 your_password 。你可以修改刚才提到的任何值,但得记住,在这个示例里,你需要将包含这些内容的每个地方都得修改一遍。

在 MySQL 中以 root 身份登录:

create database if not exists db_multitenant_SaaS;
create user if not exists vely identified by 'your_password';
grant create,alter,drop,select,insert,delete,update on db_multitenant_SaaS.* to vely;

然后在数据库内创建数据库对象(表,记录等等):

use db_multitenant_SaaS;
source setup.sql;
exit

将 Vely 连接至数据库

为了让 Vely 知晓你数据库的位置以及如何登录进去,创建一个名为 db_multitenant_SaaS 的数据库配置文件。(该名称用于在源代码中的数据库声明,所以如果你改了它,确保在它存在的每个地方都改一遍。)

Vely 使用原生的 MariaDB 数据库连接,因此你可以指定给定的数据库所能允许的任何选项:

$ echo '[client]
user=vely
password=your_password
database=db_multitenant_SaaS
protocol=TCP
host=127.0.0.1
port=3306' > db_multitenant_SaaS

构建应用程序

使用 vv 工具构建应用程序,利用 --db 选项指定 MariaDB 数据库和数据库配置文件:

$ vv -q--db=mariadb:db_multitenant_SaaS

启用应用服务器

启动你的网络应用程序的服务器,需要使用 vf FastCGI 进程管理器。应用程序服务器使用 Unix 套接字与网络服务器(创建反向代理)通信:

$ vf -w3 multitenant_SaaS

这么做会启用三个守护进程,为接收到的请求提供服务。你也可以启动一个自适应服务器,它会增加进程的数量从而服务更多的请求,并在不需要他们时减少进程的数量:

$ vf multitenant_SaaS

请参阅 vf 了解更多选项,以帮助你实现最佳性能。

当你需要停止你的应用程序服务器,使用 -m quit 选项:

$ vf -m quit multitenant_SaaS

创建网络服务器

这是一个网络应用程序,那么应用程序就得需要一个网络服务器。该示例通过一个 Unix 套接字监听器使用 Apache。

1、设置 Apache

将 Apache 配置为一个反向代理,并将你的应用程序与之连接,你需要启用 FastCGI 代理支持,这通常使用 proxyproxy_fcgi 模块。

对于 Fedora 系统(或者其它的,比如 Arch)来说,通过在 Apache 配置文件 /etc/httpd/conf/httpd.conf 中添加(或取消注释)适当的 LoadModule 指令,就可启用 proxyproxy_fcgi 模块。

以下指令适用于 Debian,Ubuntu 以及类似的系统,启用 proxyproxy_fcgi 模块:

$ sudo a2enmod proxy
$ sudo a2enmod proxy_fcgi

以下指令适用于 OpenSUSE,将这几行添加在 /etc/apache2/httpd.conf 结尾处:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

2、配置 Apache

现在你必须将代理信息添加在 Apache 的配置文件中:

ProxyPass "/multitenant_SaaS" unix:///var/lib/vv/multitenant_SaaS/sock/sock|fcgi://localhost/multitenant_SaaS

你的配置文件的位置可能会有所不同,这取决于不同的 Linux 发行版:

  • Fedora、CentOS、Mageia 和 Arch: /etc/httpd/conf/httpd.conf
  • Debian、Ubuntu、Mint: /etc/apache2/apache2.conf
  • OpenSUSE:/etc/apache2/httpd.conf

3、重新启动

最后,重启 Apache。在 Fedora 和类似系统,还有 Arch Linux 是如下指令:

$ sudo systemctl restart httpd

在 Debian 和基于 Debian 的系统,还有 OpenSUSE 是如下指令:

$ sudo systemctl restart apache2

设置本地邮箱

这个示例中,电子邮件是其功能的一部分。如果你的服务器已经可以发送电子邮件了,你可以跳过这一条。此外,你可以使用本地邮箱(myuser@localhost)来测试它。要做到这一点,需安装 Sendmail。

在 Fedora 和类似系统中是如下指令:

$ sudo dnf installsendmail
$ sudo systemctl start sendmail

而在 Debian 和类似系统(如 Ubuntu):

$ sudo apt installsendmail
$ sudo systemctl start sendmail

当应用程序向本地用户发送电子邮件,比如说 OS_user@localhost,你就可以通过查看 /var/mail/ 处(即所谓“邮件池”)来确认电子邮件是否被发送。

从浏览器访问应用服务器

假设你在本地运行该应用,可以通过使用 http://127.0.0.1/multitenant_SaaS?req=notes&action=begin 域名从你的网络服务器访问你的应用服务器。如果你在互联网上的在线服务器运行该程序,你可能就需要调整防火墙设置以允许 HTTP 通信。

源代码

该应用程序示例包含 7 个源文件。你可以自行回顾代码(记住,这些文件只有 310 行代码),下面是每个文件的概述。

SQL 设置(setup.sql)

创建的两个表:

  • users:每个用户的信息。在 users 表中,每个用户都有自己唯一的 ID (userId 列),以及其他信息,如电子邮件地址和该地址是否通过了验证。还有一个哈希密码。实际的密码永远不会存储在纯文本(或其他形式)中,单向哈希用于检查密码。
  • notes:用户输入的笔记。notes 表包含了所有的笔记,每个笔记都有一个 userId 列,表示哪个用户拥有它们。userId 列的值与 users 表中的同名列匹配。这样,每个笔记显然都属于单个用户。

该文件内容如下:

create table if not exists notes (dateOf datetime, noteId bigint auto_increment primary key, userId bigint, note varchar(1000));
create table if not exists users (userId bigint auto_increment primary key, email varchar(100), hashed_pwd varchar(100), verified smallint, verify_token varchar(30), session varchar(100));
create unique index if not exists users1 on users (email);

运行时数据(login.h)

为了正确地显示登录、注册和注销链接,你需要一些在应用程序中任何地方都可以使用的标志。此外,应用程序使用 cookie 来维护会话,因此它需要在任何地方都可用,例如,验证会话是否有效。发送到应用程序的每个请求都以这种方式进行确认。只有带有可验证 cookie 的请求是允许的。

所以要做到这种效果,你需要有一个 global_request_data 类型的 reqdata(请求数据),其中包含 sess_userId(用户的 ID)以及 sess_id(用户目前的会话 ID)。此外,还有一些不言自明的标志,可以帮助渲染页面:

#ifndef _VV_LOGIN
#define _VV_LOGIN

typedef struct s_reqdata {
    bool displayed_logout; // true 则显示登出连接
    bool is_logged_in; // true 则会话已验证登录
    char *sess_userId; // 目前会话的用户 ID
    char *sess_id; // 会话 ID
} reqdata;

void login_or_signup ();

#endif

会话检查和会话数据(\_before.vely)

Vely 里有一个 请求前处理程序 before_request handler 的概念。你写的代码会在其它处理请求的代码之前执行的。要达到这个目的,你只需要将这样的代码写在名为 _before.vely 的文件中,然后剩余的部分将会自动处理。

SaaS 应用程序所作的任何事情,例如处理发送至应用程序的请求,必须验证其安全性。这样,应用程序就能知晓调用方是否有执行操作所需要的权限。

在这里,通过请求前处理程序进行权限检查。这样,无论其他代码如何处理请求,都已经掌握了会话信息。

为保持会话数据(比如会话 ID 和用户 ID)在你代码中的任何地方都可用,你可以使用 global_request_data。它只是一个指向内存的通用指针(void*),任何处理请求的代码都可以访问它。这非常适合处理会话,如下所示:

#include "vely.h"
#include "login.h"

// _before() 是一个请求前处理程序。
// 它总是在处理请求的其他代码之前执行。
// 对于任何类型的请求范围设置或数据初始化,它都是一个很好的位置。
void _before() {
    // 输出 HTTP 请求头
    out-header default
    reqdata *rd; // 这是全局请求数据,见 login.h
    // 为全局请求数据分配内存,
    // 将在请求结束时自动释放
    new-mem rd size sizeof(reqdata)
    // 初始化标志
    rd->displayed_logout = false;
    rd->is_logged_in = false;
    // 将我们创建的数据设置为全局请求数据,
    // 可以从任何处理请求的代码中访问
    set-req data rd
    // 检查会话是否存在(基于来自客户端的 cookie)
    // 这在任何其他请求处理代码之前执行,
    // 使其更容易准备好会话信息
    _check_session ();
}

检查会话是否有效(\_check\_session.vely)

多租户 SaaS 应用程序中最重要的任务之一就是通过检查用户是否登录来(尽快)检查会话是否有效。这是通过从客户端(例如网络浏览器)获取会话 ID 和用户 ID 的 cookie,并将它们与存储会话的数据库进行比较来实现的:

#include "vely.h"
#include "login.h"


// 检查会话是否有效
void _check_session () {
    // 获取全局请求数据
    reqdata *rd;
    get-req data to rd
    // 自用户浏览器获取 cookies
    get-cookie rd->sess_userId="sess_userId"
    get-cookie rd->sess_id="sess_id"
    if (rd->sess_id[0] != 0) {
        // 检查给定用户 ID 下的会话 ID 是否正确
        char *email;
        run-query @db_multitenant_SaaS = "select email from users where userId='%s' and session='%s'" output email : rd->sess_userId, rd->sess_id row-count define rcount
            query-result email to email
        end-query
        if (rcount == 1) {
            // 如果正确,设置登录标志
            rd->is_logged_in = true;
            // 如果登出链接不显示,则显示它
            if (rd->displayed_logout == false) {
                @Hi <<p-out email>>! <a href="https://opensource.com/?req=login&action=logout">Logout</a><br/>
                rd->displayed_logout = true;
            }
        } else rd->is_logged_in = false;
    }
}

注册、登录、登出(login.vely)

任何多租户系统的基础便是具有用户注册\登录和登出的功能。通常情况下,注册包括验证电子邮件地址;不止于此,同一电子邮件地址会作为一个用户名。这里就是这种情况。

这里实现了几个执行该功能所必须的子请求:

  • 注册新用户时,显示 HTML 表单以收集信息。它的 URL 请求签名是 req=login&action=newuser
  • 作为对注册表单的响应,创建一个新用户。URL 请求的签名是 req=login&action=createuser。输入参数(input-param)信号获取 emailpwd 的 POST 表单字段。密码值是单向散列,电子邮件验证令牌是一个随机的 5 位数字。这些被插入到 users 表中,创建一个新用户。系统会发送一封验证邮件,并提示用户阅读邮件并输入代码。
  • 通过输入发送到该电子邮件的验证码来验证电子邮件。URL 请求的签名是 req=login&action=verify
  • 显示一个登录表单,让用户登录。URL 请求的签名是 req=login(例如,action 为空)。
  • 通过验证电子邮件地址(用户名)和密码登录。URL 请求的签名是 req=login&action=login
  • 应用户要求登出。URL 请求的签名是 req=login&action=logout
  • 应用程序的登录页。URL 请求的签名是 req=login&action=begin
  • 如果用户当前已登录,转到应用程序的登录页面。

可以看看下面这些例子:

#include "vely.h"
#include "login.h"

// 处理云端多租户应用程序的会话维护、登录、注销、会话验证
void login () {
    // 获取 URL 的输入参数 `action`
    input-param action

    // 获取全局请求数据,我们在其中记录会话信息,所以它很方便
    reqdata *rd;
    get-req data to rd

    // 如果会话已经建立,我们不会
    // 继续到应用程序主页的唯一原因是我们正在登出
    if (rd->is_logged_in) {
        if (strcmp(action, "logout")) {
            _show_home();
            exit-request
        }
    }

    // 应用程序页面启动。显示登录或注册的链接,
    // 并显示适当的主屏幕
    if (!strcmp (action, "begin")) {
        _show_home();
        exit-request

    // 开始创建新用户。询问电子邮件和密码,
    // 然后提交此表单时创建用户。
    } else if (!strcmp (action, "newuser")) {
        @Create New User<hr/>
        @<form action="https://opensource.com/?req=login" method="POST">
        @<input name="action" type="hidden" value="createuser">
        @<input name="email" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Email">
        @<input name="pwd" type="password" value="" size="50" maxlength="50" required placeholder="Password">
        @<input type="submit" value="Sign Up">
        @</form>

    // 验证用户发送到电子邮件的代码。代码必须匹配,从而验证电子邮件地址   
    } else if (!strcmp (action, "verify")) {
        input-param code
        input-param email
        // 获取基于电子邮件的验证令牌
        run-query @db_multitenant_SaaS = "select verify_token from users where email='%s'" output db_verify : email
            query-result db_verify to define db_verify
            // 将数据库中记录的令牌与用户提供的令牌进行比较
            if (!strcmp (code, db_verify)) {
                @Your email has been verifed. Please <a href="https://opensource.com/?req=login">Login</a>.
                // 如果匹配,更新用户信息以表明已验证。
                run-query @db_multitenant_SaaS no-loop = "update users set verified=1 where email='%s'" : email
                exit-request
            }
        end-query
        @Could not verify the code. Please try <a href="https://opensource.com/?req=login">again</a>.
        exit-request

    // 创建用户 —— 当用户使用电子邮件和密码提交表单以创建用户时运行    
    } else if (!strcmp (action, "createuser")) {
        input-param email
        input-param pwd
        // 创建散列(单向)密码
        hash-string pwd to define hashed_pwd
        // 生成随机的 5 位数字字符串验证代码
        random-string to define verify length 5 number
        // 创建用户:插入电子邮件、哈希密码、验证令牌。当前验证状态为 0,或未验证
        begin-transaction @db_multitenant_SaaS
        run-query @db_multitenant_SaaS no-loop = "insert into users (email, hashed_pwd, verified, verify_token, session) values ('%s', '%s', '0', '%s', '')" : email, hashed_pwd, verify affected-rows define arows error define err on-error-continue
        if (strcmp (err, "0") || arows != 1) {
            // 如果不能添加用户,则可能该用户不存在。不管怎样,我们都无法继续。
            login_or_signup();
            @User with this email already exists.
            rollback-transaction @db_multitenant_SaaS
        } else {
            // 创建带有验证码的电子邮件并将其发送给用户
            write-string define msg
                @From: [email protected]
                @To: <<p-out email>>
                @Subject: verify your account
                @
                @Your verification code is: <<p-out verify>>
            end-write-string
            exec-program "/usr/sbin/sendmail" args "-i", "-t" input msg status define st
            if (st != 0) {
                @Could not send email to <<p-out email>>, code is <<p-out verify>>
                rollback-transaction @db_multitenant_SaaS
                exit-request
            }
            commit-transaction @db_multitenant_SaaS
            // 通知用户查看邮件并输入验证码
            @Please check your email and enter verification code here:
            @<form action="https://opensource.com/?req=login" method="POST">
            @<input name="action" type="hidden" value="verify" size="50" maxlength="50">
            @<input name="email" type="hidden" value="<<p-out email>>">
            @<input name="code" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Verification code">
            @<button type="submit">Verify</button>
            @</form>
        }

    // 这里在登录用户登出时运行    
    } else if (!strcmp (action, "logout")) {
        // 更新用户表以清除会话,即没有该用户登录
        if (rd->is_logged_in) {
            run-query @db_multitenant_SaaS = "update users set session='' where userId='%s'" : rd->sess_userId no-loop affected-rows define arows
            if (arows == 1) {
                rd->is_logged_in = false; // 提示用户未登录
                @You have been logged out.<hr/>
            }
        }
        _show_home();

    // 登录:当用户输入用户名和密码时运行
    } else if (!strcmp (action, "login")) {
        input-param pwd
        input-param email
        // 创建单向散列,目的是与用户表进行比较 —— 密码**永远不会**被记录
        hash-string pwd to define hashed_pwd
        // 为会话 ID 创建一个随机的 30 位长的字符串
        random-string to rd->sess_id length 30
        // 检查用户名和哈希密码是否匹配
        run-query @db_multitenant_SaaS = "select userId from users where email='%s' and hashed_pwd='%s'" output sess_userId : email, hashed_pwd
            query-result sess_userId to rd->sess_userId
            // 如果匹配,使用会话 ID 更新用户表
            run-query @db_multitenant_SaaS no-loop = "update users set session='%s' where userId='%s'" : rd->sess_id, rd->sess_userId affected-rows define arows
            if (arows != 1) {
                @Could not create a session. Please try again. <<.login_or_signup();>> <hr/>
                exit-request
            }
            // 设置“用户 ID”和“会话 ID”为 cookie。用户的浏览器将在每个请求中返回这些信息
            set-cookie "sess_userId" = rd->sess_userId
            set-cookie "sess_id" = rd->sess_id
            // 显示主页,确保会话是正确的,并设置标志
            _check_session();
            _show_home();
            exit-request
        end-query
        @Email or password are not correct. <<.login_or_signup();>><hr/>

    // 登录界面,要求用户输入用户名和密码  
    } else if (!strcmp (action, "")) {
        login_or_signup();
        @Please Login:<hr/>
        @<form action="https://opensource.com/?req=login" method="POST">
        @<input name="action" type="hidden" value="login" size="50" maxlength="50">
        @<input name="email" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Email">
        @<input name="pwd" type="password" value="" size="50" maxlength="50" required placeholder="Password">
        @<button type="submit">Go</button>
        @</form>
    }
}

// 显示登录或注册链接
void login_or_signup() {
        @<a href="https://opensource.com/?req=login">Login</a> & & <a href="https://opensource.com/?req=login&action=newuser">Sign Up</a><hr/>
}

通用应用程序(\_show\_home.vely)

借助本教程,你可以创建你想要的任何多租户 SaaS 应用程序。上面的多租户处理模块(login.vely)调用 _show_home() 函数,它可以容纳你的任何代码。这个示例代码展示了笔记应用程序,但它可以是任何内容。_show_home() 函数可以调用你想要的任何代码,它是一个通用的多租户应用程序插件:

#include "vely.h"

void _show_home() {
    notes();
    exit-request
}

笔记应用程序(notes.vely)

该应用程序能够添加、列举以及删除任何给定的笔记:

#include "vely.h"
#include "login.h"

// 多租户云中的笔记应用程序
void notes () {
    // 获取全局请求数据
    reqdata *rd;
    get-req data to rd
    // 如果会话有效,显示登录或注册
    if (!rd->is_logged_in) {
        login_or_signup();
    }
    // 问候用户
    @<h1>Welcome to Notes!</h1><hr/>
    // 如果没有登出,退出 —— 这里确保对用户身份的安全验证
    if (!rd->is_logged_in) {
        exit-request
    }
    // 获取 URL 参数,告诉笔记要做什么
    input-param subreq
    // 显示笔记能够做什么操作(添加或列举笔记)
    @<a href="https://opensource.com/?req=notes&subreq=add">Add Note</a> <a href="https://opensource.com/?req=notes&subreq=list">List Notes</a><hr/>

    // 列举该用户的所有笔记
    if (!strcmp (subreq, "list")) {
        // **只**选取该用户的笔记
        run-query @db_multitenant_SaaS = "select dateOf, note, noteId from notes where userId='%s' order by dateOf desc" : rd->sess_userId output dateOf, note, noteId
            query-result dateOf to define dateOf
            query-result note to define note
            query-result noteId to define noteId
            // 使用快速缓存正则表达式将新行更改为<br/>
            match-regex "\n" in note replace-with "<br/>\n" result define with_breaks status define st cache
            if (st == 0) with_breaks = note; // 什么都没有发现/替换,只用原来的
            // 显示笔记
            @Date: <<p-out dateOf>> (<a href="https://opensource.com/?req=notes&subreq=delete_note_ask&note_id=%3C%3Cp-out%20noteId%3E%3E">delete note</a>)<br/>
            @Note: <<p-out with_breaks>><br/>
            @<hr/>
        end-query
    }

    // 要求删除笔记
    else if (!strcmp (subreq, "delete_note_ask")) {
        input-param note_id
        @Are you sure you want to delete a note? Use Back button to go back, or <a href="https://opensource.com/?req=notes&subreq=delete_note&note_id=%3C%3Cp-out%20note_id%3E%3E">delete note now</a>.
    }

    // 删除笔记
    else if (!strcmp (subreq, "delete_note")) {
        input-param note_id
        // 删除笔记
        run-query @db_multitenant_SaaS = "delete from notes where noteId='%s' and userId='%s'" : note_id, rd->sess_userId affected-rows define arows no-loop error define errnote
        // 告知用户状态
        if (arows == 1) {
            @Note deleted
        } else {
            @Could not delete note (<<p-out errnote>>)
        }
    }

    // 添加笔记
    else if (!strcmp (subreq, "add_note")) {
        // 从 note 表单中获取 URL POST 数据
        input-param note
        // 在该用户的 ID 下插入笔记
        run-query @db_multitenant_SaaS = "insert into notes (dateOf, userId, note) values (now(), '%s', '%s')" : rd->sess_userId, note affected-rows define arows no-loop error define errnote
        // 告知用户状态
        if (arows == 1) {
            @Note added
        } else {
            @Could not add note (<<p-out errnote>>)
        }
    }

    // 显示一个 HTML 表单来收集笔记,并将其发送回这里(使用 subreq="add_note" URL 参数)
    else if (!strcmp (subreq, "add")) {
        @Add New Note
        @<form action="https://opensource.com/?req=notes" method="POST">
        @<input name="subreq" type="hidden" value="add_note">
        @<textarea name="note" rows="5" cols="50" required autofocus placeholder="Enter Note"></textarea>
        @<button type="submit">Create</button>
        @</form>
    }
}

具有 C 性能的 SaaS

Vely 语言使得 C 语言在你的网络应用程序中得到充分利用这件事成为可能。多租户 SaaS 应用程序便是从中受益的一个典型用例。

看一看参考代码示例,写一写代码,然后试试 Vely。

(题图:DA/126624c8-1a47-481b-b149-92273e8e0f4f)


via: https://opensource.com/article/22/11/build-your-own-saas-vely

作者:Sergio Mijatovic 选题:lkxed 译者:Drwhooooo 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

1 Go 程序员大多使用 Linux 或 MacOS

Go 团队在八月份对 Go 开发者进行了一项调查,调查结果显示 90% 的受访者表示他们在上一年使用 Go 时感到满意。他们在 Linux(63%)和 macOS(58%)系统上使用 Go 语言,但新的 Go 程序员更倾向于使用 Windows。虽然 x86 兼容系统仍占开发的大多数(89%),但 ARM64 现在也被大多数受访者使用(56%),这种采用似乎部分是由苹果芯片推动的。受访者最喜欢的代码编辑器是 VS Code(44%)、GoLand(31%)、Vim/Neovim(16%)和 Emacs(3%)。3/4 的受访者在使用云服务的 Go 软件上工作,这表明开发人员认为 Go 是一种适用于现代云开发的语言。

(插图:DA/3cf4022f-9a7a-460b-8be4-741c10daa2f7)

消息来源:Go Dev
老王点评:使用 Linux 的比 macOS 和 Windows 的要多,但是为什么使用 VS Code 的最多呢。

2 欧盟就《人工智能法》的推出达成共识

来自欧洲议会及其 27 个成员国的谈判代表克服了巨大分歧,签署了《人工智能法》的临时政治协议。争议点主要是生成式人工智能和警方使用面部识别监控等方面。这一结果是在上周举行的马拉松式闭门会谈后达成的,其中一次会谈持续了 22 个小时,“欧盟成为第一个为人工智能的使用制定明确规则的大陆”。关于最终法律的具体内容,官员们提供的细节很少,该法律最早要到 2025 年才能生效,预计将为进一步谈判留出余地,以制定出更细的条款。

(插图:DA/04fef112-325d-43c4-a6b1-1731cd9866b8)

消息来源:AP News
老王点评:就怕立法追不上 AI 的发展速度。

3 让网速感觉更快的新标准 L4S

这项名为 L4S 的新互联网标准已于今年 1 月定稿并发布,它可以大大减少我们等待网页或流媒体加载的时间,并减少视频通话中的故障。它还有助于改变我们对网速的看法,并帮助开发人员创建在当前互联网现实情况下无法实现的应用程序。L4S 是 低延迟、低损耗、可扩展吞吐量 Low Latency, Low Loss, Scalable Throughput 的缩写,其目标是通过减少排队的需要,确保数据包减少不必要的排队等待,花费尽可能少的时间。在某些情况下,数据包缓冲延迟通常为数百毫秒甚至数千毫秒,而 L4S 则可以将延迟时间降至几毫秒。更好的是,它与目前使用的拥塞控制系统广泛兼容。虽然 L4S 还没有被广泛使用,但苹果、谷歌、英伟达、爱立信、德国电信等公司都已经对此表示了极大兴趣。

(插图:DA/cdd2468a-758a-487e-b5bd-b908700ce05e)

消息来源:The Verge
老王点评:为什么这么好的协议改进,我居然之前都没听说过。

去中心化开源平台 PeerTube 大升级,这是一个很好的 YouTube 替代品。

曾经想过让你的视频平台摆脱大型科技公司的控制吗?

嗯,对于许多人来说,PeerTube 是一个相当不错的选择,因为它具有开源和自托管的性质。由 Framasoft 开发,开发人员一直忙于定期向 PeerTube 添加新功能。

当然,作为一个去中心化平台,它可能无法为你提供与 YouTube 相同的用户体验(或质量)。然而,它是 最好的去中心化开源平台 之一。

这次,他们推出了一个新版本,即 PeerTube V6,并承诺将进行大量改进。 请允许我带你了解这些。

? PeerTube V6:有什么新内容?

The PeerTube Mascot for V6.

PeerTube V6 版本是一个主要版本,根据从用户那里收到的反馈,引入了社区最需要的一些功能

一些关键亮点包括:

  • 视频章节
  • 视频故事板
  • 替换上传的视频
  • 视频密码保护

视频章节

你现在可以向视频添加章节以指定视频的不同部分。这是一个非常方便的小功能,增强了观看者的体验,使他们能够看到视频的内容。

正如你在上面看到的,在章节设置了特定的标题和时间码后,会出现标记将进度条分为不同的部分。

视频故事板

V6 版本带来的另一个功能是能够通过将鼠标悬停在进度条上或拖动进度条来预览视频

这也被称为“故事板”功能,它在大多数流行的视频流平台上都有,现在 PeerTube 也拥有它。

替换上传的视频

有时会发生错误,错误编辑的视频会呈现给观众。幸运的是,有了 PeerTube V6,这一切都成为了过去。

用户现在只需点击几下即可选择重新上传视频以替换旧视频。这样做时,URL、标题、信息、统计数据和评论将保持不变。

但是,此功能必须由 PeerTube 实例的管理员启用才能工作。被替换的视频将被贴上“视频重新上传”标签。

视频密码保护

你现在可以决定为视频设置密码以限制访问或分发专有内容。

管理员和开发人员可以通过 PeerTube 的内部 REST API 获得更大的灵活性,他们可以设置和存储任意数量的密码,从而更轻松地授予/撤销对视频的访问权限。

?️ 其他更改和改进

除了上述亮点之外,还有一些值得注意的改进:

  • 更好的远程运行管理。
  • SEO 和视频链接共享已得到改进。
  • 针对辅助功能和键盘导航的各种修复。
  • 视频播放器已得到改进,播放新视频时不再重新加载并保存当前播放器设置。

你可以通过官方的 发行说明公告博客 了解更多信息。

? 获取 PeerTube V6

你可以前往官方网站开始使用 PeerTube V6。

PeerTube

对于现有用户,你可以按照 官方文档 升级你的 PeerTube。

看到谷歌越来越 积极 地将 YouTube 平台货币化,我们很高兴看到像 PeerTube 这样的由社区驱动平台依然存在。

? PeerTube 正在成为一个不错的 YouTube 替代品,你觉得怎么样?

(题图:DA/117fe978-0b8e-4183-b580-72fea7938b02)


via: https://news.itsfoss.com/peertube-v6/

作者:Sourav Rudra 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在日本开源峰会中,Linux 和 Git 的创始人 Linus Torvalds 深入探讨了 Rust 在 Linux 中的应用、Linux 维护者压力问题,以及 AI 对 Linux 和开源开发未来的影响。

尽管 Linux 的创始人 Linus Torvalds 最近鲜少公开露面,但在 Linux 基金会的 日本开源峰会 上,他与其好友,Verizon 开源部门负责人 Dirk Hohndel 共同探讨了 Linux 的当前状况。

首先,两人对下一个 Linux 内核版本 Linux 6.7 进行了讨论。在出发前往东京前,Torvalds 已经发布了此版本的 第四个发布候选版。这意味着,如果一切顺利,且 Torvalds 未发现任何问题,我们将在圣诞节左右看到新版本的 Linux 内核。

正如 Torvalds 解释的那样,他不希望把 “合并窗口” 放在圣诞,因为这会 “毁掉我的圣诞节”。而现在,“我们正在等待,以确保不存在任何可能成为绊脚石的问题”。为确保正在为下一个版本 6.8 而准备的维护者和开发者们不会因为知道 “圣诞节后我的合并窗口将开启而陷入 恐慌 Panic ,我们可能会将其推迟一到两周,使时间安排更为舒适,因为没有人愿意在圣诞期间繁重的工作。”

当提到维护者的话题,Hohndel 提出了 “维护者疲劳以及这个角色的疲劳和压力” 的讨论。正如我最近的报道,Linux 内核的维护者对于这个关键而高要求的角色感到 压力渐增

对此,Torvalds 做出回应:“找到开发者比较容易;我们有很多的开发者。一些人认为,只有能做任何事情的超级开发者才有资格成为维护者,但实际情况并非如此。”

Torvalds 接着说,“要成为一名维护者,你需要有足够的鉴赏力来评判别人的代码。有些能力可能是与生俱来的,但大部分则需要通过实践来获得。你必须能看别人的代码,并能区分,‘这是好的实践还是坏的实践’?通常这只是多年实践的结果。”

尽管如此,Torvalds 还是强调,“我们有很多优秀的维护者,但另一方面,你必须坚守在岗位,或者需要找到能够和你协同工作的维护者,这样你就能规划好你的休假和其他事项。”

对于 Torvalds 来说,“始终在岗并不是问题,因为我热爱我所做的事情。几个月前我去度假,我带了我的笔记本电脑。如果我没有随身带着笔记本电脑,我可能会感到无比无聊。这就是我生活的方式,但我明白并不是每个人都适合这样的生活,尤其是当你要投入生活中好几年的时间时。”

这也是 Torvalds 需要积极学习和提升的一部分,“代码容易写,有对错可寻。但人际关系则较为复杂,必须学会与开发者或者维护者相处,尤其当维护者都有各自不同的目标。他们想将自己负责的区域推向一个方向,而其他的维护者可能会想要将它朝其他方向引导。这种情况会带给人巨大的压力。”

在 2018 年,Torvalds 决定放弃他愤怒的态度,他 休假一段时间,去改正他对其他开发者的态度。有所改观后,Torvalds 回归 了 Linux 内核工作。自从那时起他变得更为温和,正如他在东京提到的,他不再 “对某公司竖中指,我已吸取了教训。”

总结一下,Torvalds 指出,“人们往往认为开源全是关于编程的事,但实际上很多是与沟通相关。维护者就是翻译的人,我不仅指的是语言,更是代码的环境,代码存在的理由。这是一项艰巨的任务。但是,如果你想成为维护者,相信我,顶层总有你的位置。”

此外,Linux 内核社区的老龄化也是一个值得关注的问题。如 Hohndel 所说,“如果我看五年后,很多 (顶尖的 Linux 内核)的人们将步入 60 岁,甚至有人将接近 70 岁。”

对此,Torvalds 承认,“我们中的很多人都在步入老年,但部分原因是因为我们有一些已经工作超过 30 年的维护者。他们依然活跃,仍然会回来找我。我们拥有一个人们愿意长期坚持的社区。”

Hohndel 评论道,内核社区的老龄化问题是枚 “双刃剑”,Torvalds 同意这个观点,并指出,“我喜欢内核中的 Rust 的原因之一是,那里有一个明显比其他维护者年轻很多的维护者。我们可以明显看到内核的某些区域更能吸引年轻人。”比如在驱动方面,那里更容易找到年轻的人,这一直是我们发展和培养维护者的传统方式,包括 Greg(Korah-Hartman,Linux 稳定内核的维护者)。

Hohndel 和 Torvalds 还谈到了在 Linux 内核中使用 Rust 语言的情况,Torvalds 指出,“这方面有增长,但我们的内核还没有哪个部分真正依赖 Rust。对我而言,Rust 在技术上是有意义的,但以我个人的看法,更重要的是我们不能因为是内核和开发者就停滞不前。”

Torvalds 继续说道:“尽管 Rust 还未真正展现出它的巨大潜力,但我想在明年,我们将开始集成开始积极使用它的驱动程序和一些甚至是主要子系统。所以这是一种需要几年才能在内核中占有重要地位的事情,但无疑,它正在逐渐塑造出这一未来。”

展望未来,Hohndel 谈到我们必须去考虑大型语言模型(LLM)人工智能。他认为人工智能更像是把超级自动更正,因为其实大型语言模型的核心功能就是预测你下一个最可能用到的词,然后从此处进行推理。尽管它看似并不真正聪明,但显然,它对我们的生活以及我们生活的现实产生了深远影响。他问道:“你觉得我们会看到有人提交由大型语言模型写出的代码吗?”

Torvalds 的答复坚定且直接,“我确信这种情况会发生,甚至可能已经发生了。也许现在是在一个较小的范围内,人们更多的是在利用它来辅助编写代码。”但是,和许多人不同的是,Torvalds 并不对人工智能感到担忧。“自动化帮助编写代码的情况显然一直存在,这并不是什么新鲜事。”他说。

实际上,Torvalds 希望 AI 能在“寻找明显的愚蠢错误方面提供帮助,因为很多他看到的错误并不是难以注意的错误,很多都是愚蠢的错误,这并不需要任何更高级的智能才能发现。”他希望有更多工具能在错误更难以发现的情况下发出警告,比如,“这种模式看起来不太常见,你确定这是你想要的吗?” 答案也许是 “不,这不是我的意思,你找到了明显的问题,非常感谢!”我们确实需要一款超级自动更正。他看待 AI 更像是一个可以帮助我们在做好自己事情的一项工具。

Hohndel 接着提问:“那关于 AI 带来的幻觉呢?”对此,一向坦率直言的 Torvalds 表达了他的看法,“我每天都会看到即使没有 AI 的情况下也依然会发生的错误。所以这就是为什么我并不太担心。我认为我们自己仍然很擅长犯错误。”

随后,Torvalds 表达了他对于开源的热爱,“我很高兴开源、开放的理念如今获得了更广泛的接受。我特别记得 30 年前我刚开始这个项目时,人们会质疑我,问我‘为什么呢?你又是怎么盈利的呢?’ 这种问题现在已经不再出现,开源已经成为了这个行业的标准,不论是编程还是数据,大规模的项目需要在公司之间分享,这已经成为了人们的共识。”

Hohndel 指出,“Linux 基金会的目标就是鼓励超越个人、公司,甚至超越整个社会,在一个中立的地方进行合作。在这样一个中立而公正的场所,人们可以聚集在一起实现一些事情,这是非常重要的。”

最后,Torvalds 总结道,“这就是我为什么在 Linux 基金会工作,因为我拒绝在任何 Linux 公司工作。我不想让任何一个公司或任何一个商业实体成为特殊地位。我们需要一个中立的地方,这就是为什么我决定把我的姓名给了 Linux 基金会。”

(题图:DA/e811695a-aa34-4805-b634-03c516688323)


via: https://www.zdnet.com/article/linus-torvalds-on-state-of-linux-today-and-how-ai-figures-in-its-future/

作者:Steven Vaughan-Nichols 译者:ChatGPT 校对:wxy

1 Linus Torvalds 谈 Rust 和 AI 编程

在 Linux 基金会的日本开源峰会上,Linus Torvalds 在访谈中谈到了在 Linux 内核中使用 Rust 语言的问题。Torvalds 说:“它一直在增长,但内核还没有任何部分真正依赖 Rust。……Rust 还没有真正显示出它是下一个伟大的事物。……要让它成为内核的重要组成部分,还需要数年时间。但它肯定会成为内核的一部分。”我们都知道 Torvalds 对 Rust 进入内核大开绿灯,他认为 “Rust 是技术上有意义的事情之一,……更重要的是,作为内核和开发人员,我们不能停滞不前”。有趣的是,还一个原因是“有一位(Rust)维护者明显比大多数维护者年轻得多。”

他也谈到了对 AI 编程的看法,当被问到是否认为会看到提交由 AI 编写的代码时,他说:“我相信这一定会发生。而且很可能已经发生了,也许规模较小,人们更多使用它来帮助编写代码。”与许多人不同,Torvalds 并不太担心人工智能,“很明显,自动化一直在帮助人们编写代码。这根本不是什么新鲜事。”而对于 AI 幻觉问题,他说:“我每天都能看到没有 AI 而出现的 bug。所以我才不那么担心。我认为,我们自己犯错误的能力还不错。”

(插图:DA/4794e383-cb3e-421e-876b-73720b421b57)

消息来源:ZDNet
老王点评:Linux 内核有这样一位自信而开放的领袖,确实幸事。

2 Meta 用 11 亿张用户照片训练其人工智能图像生成器

Meta 推出了一项独立的文本到图像人工智能生成器服务 Imagine,该服务建立在其名为 Emu 的人工智能模型上,这个模型是在 11 亿张 Facebook 和 Instagram 用户照片上训练出来的。Instagram 上每天上传的照片数以亿计,训练的照片只占了很小一部分。Meta 称排除了私人信息和未在其服务上公开分享的图片。不过,尽管 Meta 公司大力支持开源人工智能,但 Emu 和 Imagine 目前都还没有开源。

(插图:DA/c9b0a5ca-2f10-4467-bdac-0af3a1eba5cb)

消息来源:Venture Beat
老王点评:所以,公开上传的图片其实就是很好的 AI 饲料。

3 微软 DHCP 服务器软件被滥用来欺骗 DNS 记录

据 Akamai 安全研究人员称,只需对运行微软 DHCP 服务器默认配置的服务器发动攻击,就可能会让攻击者欺骗 DNS 记录、入侵活动目录并窃取其存储的所有机密。虽然目前还没有看到服务器受到这种类型的攻击,但在 Akamai 监控的数千个网络中,有 40% 的网络正在使用带有漏洞的默认 DHCP 配置,因此大量的企业都有可能受到攻击。微软 DHCP 服务器默认启用了 DHCP DNS 动态更新,这就是问题所在,除了创建不存在的 DNS 记录外,无需认证的攻击者还可以使用 DHCP 服务器覆盖现有数据。不过,微软接到报告后表示不打算采取动作,而 Akamai 将在不久的将来发布实现这些攻击的代码。

(插图:DA/e8fb1f7a-b4c8-41c2-afd4-e4bb55f8e2ce)

消息来源:The Register
老王点评:默认配置不安全难道不该修复,或者至少发个安全公告吗?

Daimar Steiner 摄

本文介绍了 Flathub 中可用的项目以及安装说明。

Flathub 就是为所有 Linux 发行版提供应用程序的平台,其由 Flatpak 支持,确保 Flathub 上的应用能在绝大多数 Linux 发行版上运行。

请参阅 Flatpak 入门,并按照 flatpak 网站的指南激活 Flathub 作为你的 Flatpak 供应商。

Live Captions

Live Captions 是一款为 Linux 桌面提供实时自动字幕的应用。目前仅提供英语。其他语言可能会产生乱码或错误的语音翻译。

主要特性:

  • 使用友好的界面
  • 基于深度学习,为本地桌面/麦克风音频添加字幕
  • 不需要 API 密钥、不依赖专有服务/组件、无追踪、无数据收集、不使用互联网权限
  • 支持调整字幕字体、字体大小,以及进行大小写转换
  • 不确定的文本会显示为暗色(此功能可进行设置)。

老实说,直到最近有人推荐我才知道这个应用,我感到非常惊讶。这对于有听力问题的人来说确实很有帮助。正如此类软件的常见情况,模型需要培训,而项目通常需要反馈。

Live Captions 需要良好的硬件才能正常运行,但不需要专业的 GPU。

该项目被标记为安全,因为它不需要特殊许可:

你可以通过单击网站上的安装按钮或手动使用以下命令来安装 “Live Caption”:

flatpak install flathub net.sapples.LiveCaptions

Pencil2D

Pencil2D 是一个 2D 动画创建程序,可让你使用位图和矢量图形轻松创建手绘图形。

主要特性:

  • 轻量设计
  • 支持光栅和矢量图
  • 跨平台兼容
  • 自由开源

请注意,该项目被标记为“潜在不安全”,因为它可以访问你的文件系统:

你可以通过单击网站上的安装按钮或手动使用以下命令来安装 “Pencil2D”:

flatpak install flathub org.pencil2d.Pencil2D

Pencil2D 也在 Fedora 的仓库中以 RPM 形式提供

Frog

Frog 是一款应用,它可以帮助你从图片、网站、视频和二维码中提取文本。

主要特性:

  • 提取二维码和条形码:能够快速捕获、提取并转译二维码和条形码。
  • 你可以直接拖放图像到 Frog 窗口中提取文本,无需进行截图。
  • 支持大量语言:Frog 支持多种语言,甚至包括那些它以前不支持的语言。
  • 隐私:Frog 使用门户网站尊重你的隐私

请注意,该项目被标记为“潜在不安全”,因为它可以访问你的主文件夹:

你可以通过单击网站上的安装按钮或手动使用以下命令来安装 “Frog”:

flatpak install flathub com.github.tenderowl.frog

PDF Arranger

PDF Arranger 是一个小型应用,它可以帮助用户使用交互式直观的图形界面合并或拆分 PDF 文档并旋转、裁剪和重新排列其页面。

其简明的界面和易于使用的功能都非常出色。

请注意,该项目被标记为“潜在不安全”,因为它可以访问你的文件系统:

你可以通过单击网站上的安装按钮或使用以下命令手动安装 “PDF Arranger”:

flatpak install flathub com.github.jeromerobert.pdfarranger

PDF Arranger 也在 Fedora 的仓库中以 RPM 形式提供。


via: https://fedoramagazine.org/fedora-linux-flatpak-cool-apps-to-try-for-december/

作者:Eduard Lucena 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出