OpenAPI 规范(OAS)介绍 - Bearalise

背景

作为一名开发者,都需要编写程序的 API 文档。现代开发框架,都需要API的协作,有时完成一个操作,需要调用多个API才能完成,所以,一个好的 API 文档能够大大提高协作效率,降低沟通成本。为解决API文本规范的问题,我们聊聊如何使用 OpenAPI 构建 HTTP 接口文档。

OpenAPI 规范(OAS)介绍

OpenAPI 是规范化描述 API 领域应用最广泛的行业标准,由 OpenAPI Initiative(OAI) 定义并维护,同时也是 Linux 基金会下的一个开源项目。通常我们所说的 OpenAPI 全称应该是 OpenAPI Specification(OpenAPI 规范,简称 OSA),它使用规定的格式来描述 HTTP RESTful API 的定义,以此来规范 RESTful 服务开发过程。使用 JSON 或 YAML 来描述一个标准的、与编程语言无关的 HTTP API 接口。OpenAPI 规范最初基于 SmartBear Software 在 2015 年捐赠的 Swagger 规范演变而来,目前最新的版本是 v3.1.1。本文参考了apifox 发布的一个 中文版

OpenAPI 规范基本信息

文档结构

一份 OpenAPI 文档可以是单个文件也可以被拆分为多个文件, 连接的部分由用户自行决定。在后一种情形下,必须如 JSON Schema 中定义的那样使用 $ref 字段来相互引用。

推荐将根 OpenAPI 文档命名为openapi.json 或 openapi.yaml

More...

如何利用Cloudflare搭建图床(+ Piclist快速上传)- Bearalise

背景

写博客就要有图床,之前搭了一个国内版的,今天尝试用Cloudflare搭建一个海外版的。

开通Cloudflare R2

R2 是 Cloudflare 推出的免费对象存储服务,需要免费注册一个Cloudflare 账号才能使用,注册登录后,点击左侧边栏的 R2 访问服务,但需要注意的是开通 R2 服务需要绑定信用卡(国内外主流信用卡皆可),但并不会扣费,主要是为了验证用户身份使用:

创建图床 Bucket

Bucket即为存储桶,所有的资源的是需要放在存储桶中。开通 R2 服务后,点击右上角「创建存储桶」按钮进行创建:

起一个名字,其他选项如图:

开通后显示以下信息:

More...

如何用pgloader同步数据库 - Bearalise

背景

之前开发时就发现,mysql和pg数据库 没有合适的数据库同步工具,只能用dump将数据库dump下来,再重新upload到新的库

解决方案

最近发现一个比较好的工具:Pgloader ,下面是具体步骤:

  1. 安装: sudo apt-get install pgloader

  2. 编辑同步配置 pg.load

    1
    2
    3
    load database
    from pgsql://localhost/pgloader
    into pgsql://localhost/copy;
  3. 运行

    1
    pgloader pg.load

运行成功,目前看起来还是十分简单好用的。

More...

利用AlertDialog实现二次确认按钮 - Bearalise

背景

在用户点击某个按钮后弹出 AlertDialog,如果用户确认,则执行原本的点击动作;如果用户取消,则不执行该动作。这是一个很常见的模式,用于防止用户误操作。这里给出用AlertDialog组件的一个实现。

关键点

  1. 我们定义了一个 performDangerousAction 函数,它代表您想要执行的实际操作。在这个例子中,它只是设置了一个结果状态,但在实际应用中,这里可能是一个 API 调用或其他重要操作。
  2. 当用户点击 “执行危险操作” 按钮时,我们不直接执行操作,而是打开 AlertDialog。
  3. 在 AlertDialog 中:
    1. 如果用户点击 “确认”,我们调用 handleConfirm 函数,它会执行危险操作并关闭对话框。
    2. 如果用户点击 “取消”,我们调用 handleCancel 函数,它会设置一个取消消息并关闭对话框。
  4. 我们使用 actionResult 状态来显示操作的结果,无论是执行了还是被取消。
  5. AlertDialog 的可见性由 isOpen 状态控制,这允许我们在需要时打开和关闭它。

完整代码

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
'use client'

import { useState } from "react"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"

export default function AlertDialogWithAction() {
const [isOpen, setIsOpen] = useState(false)
const [actionResult, setActionResult] = useState<string | null>(null)

// 模拟一个危险操作
const performDangerousAction = () => {
// 这里是实际的危险操作逻辑
setActionResult("危险操作已执行!")
}

const handleConfirm = () => {
performDangerousAction()
setIsOpen(false)
}

const handleCancel = () => {
setActionResult("操作已取消")
setIsOpen(false)
}

return (
<div className="space-y-4">
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
<AlertDialogTrigger asChild>
<Button
variant="destructive"
onClick={() => setIsOpen(true)}
>
执行危险操作
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>您确定要执行此操作吗?</AlertDialogTitle>
<AlertDialogDescription>
此操作可能会导致不可逆的结果。请确认您真的要执行此操作。
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={handleCancel}>取消</AlertDialogCancel>
<AlertDialogAction onClick={handleConfirm}>确认</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{actionResult && (
<div className="mt-4 p-4 bg-muted rounded-md">
<p>操作结果: {actionResult}</p>
</div>
)}
</div>
)
}

作者:Bearalise
出处:https://blog.952005.xyz/2024/11/22/how-to-use-component-to-double-confirm/
版权:本文版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明,必须在文章中给出原文链接。

如何修改HTML默认上传按钮组件样式 - Bearalis

背景

系统自带的上传按钮有点丑,并且大的样式无法修改,想了个方法用Tailwind的按钮包装了系统自带的按钮,实现了样式的统一

解决方案

建立Uploadbutton 组件:

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

import React, { ChangeEvent,useRef } from 'react';

import { Button } from '@/components/ui/button';
import { TreeDataItem } from '@/lib/utils';

interface Question {
...
}

interface Option {
...
}

type Props = {
disabled: boolean;
onUpload: ( result: any ) => void;
};

const UploadButton = ( { disabled, onUpload }: Props ) => {
const fileInputRef = useRef<HTMLInputElement | null>(null);

const handleButtonClick = () => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
};

const parseQuestions = (content: string): { questions: Question[] } => {
...
};

const generateTreeData = ( questions: Question[]) : { data: TreeDataItem[] } => {
...
};

const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {

const file = e.target.files?.[0];
if (!file) return;

try {
const content = await file.text();

const { questions } = parseQuestions(content);
const { data } = generateTreeData(questions);

const results = {
data: data,
jsondata: questions
};

onUpload( results );
} catch (error) {
console.error('Error processing file:', error);
}
}

return (
<div className="w-full lg:w-auto">
<input
type="file"
ref={fileInputRef}
accept=".txt"
onChange={handleFileChange}
className="hidden"
/>
<Button
disabled={disabled}
onClick={handleButtonClick}
size="sm"
className="w-full lg:w-auto"
>
Import TXT
</Button>
</div>
);
};

export default UploadButton;

分解来说,

  1. 定义组件,并定义onUpload 函数作为组件参数
  2. 定义 fileInputRef ,绑定给原始上传控件
  3. 再定义 handleButtonClick 函数,函数里调用上传控件的Click方法
  4. 定义Button控件,将点击事件绑定给handleButtonClick
  5. 定义handleFileChange函数,绑定原始上传控件的onChange事件
  6. 在handleFileChange函数,用 await file.text() 得到文件内容,并处理加工,把结果通过onUpload 函数,传输给外部调用者

作者:Bearalise
出处:如何修改HTML默认上传按钮组件样式 - Bearalis
版权:本文版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明,必须在文章中给出原文链接。

PostgreSQL 在Windows环境下使用 Docker 安装 - Bearalise

背景

项目需要使用PG, 尝试安装一下

Create Volume

1
docker volume create --driver local --opt device=C:\10.VM\data\pg01 --opt type=none --opt o=bind pg01

Pull Image

1
docker pull postgres

Start Container

1
2
docker run --name pgsql --privileged -e POSTGRES_PASSWORD=xxxxx -p 15333:5432 -v pg01:/var/lib/postgresql/data -d postgres

连接数据库,创建用户

1
2
create user fintest01 with password 'xxxxxx';
create database fintest01 with owner fintest01;

切换数据库连接

1
DATABASE_URL=postgresql://fintest01:[email protected]:15333/fintest01
More...

Mac OS 下修改 Google Chrome 显示语言的方法

背景

由于调试程序需要,希望把Chrome的语言改成英文,发现在Mac下Chrome的语言是跟随系统语言的,通过菜单无法修改。

解决方案

英文 -> 简体中文

defaults write com.google.Chrome AppleLanguages '(zh-CN)'

简体中文 -> 英文

defaults write com.google.Chrome AppleLanguages '(en-US)'

英文优先,简体中文第二。反之改一下顺序

defaults write com.google.Chrome AppleLanguages "(en-US,zh-CN)"

Build a Finance SaaS Platform -26 (Account/Date Filter)(End)

Install Package

1
npm i query-string

Add Filter

Add components/filters.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react'
import { AccountFilter } from '@/components/account-filter'
import { DateFilter } from '@/components/date-filter'

export const Filters = () => {
return (
<div className="flex flex-col lg:flex-row items-center gap-y-2 lg:gap-y-0 lg:gap-x-2">
<AccountFilter />
<DateFilter />
</div>
)
}

More...

Build a Finance SaaS Platform -25 (Spending Pie)

Add Spending Pie Component

Add components/spending-pie.tsx

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

import { useState } from "react";
import { FileSearch, Loader2, PieChart, Radar, Target } from "lucide-react";

import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"

import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";

import { PieVariant } from "@/components/pie-variant";
import { RadialVariant } from "@/components/radial-variant";
import { RadarVariant } from "./radar-variant";
import { Skeleton } from "./ui/skeleton";


type Props = {
data?: {
name: string;
value: number;
}[];
};

export const SpendingPie = ( { data = []} : Props) => {
const [chartType, setChartType] = useState("pie");

const onTypeChange = ( type: string) => {
// TODO: Add paywall
setChartType(type);
}
return(
<Card className="border-none drop-shadow-sm">
<CardHeader className="flex space-y-2 lg:space-y-0 lg:flex-row lg:items-center justify-between">
<CardTitle className="text-xl line-clamp-1">
Categories
</CardTitle>
<Select
defaultValue={chartType}
onValueChange={onTypeChange}
>
<SelectTrigger className="lg:w-auto h-9 rounded-md px-3">
<SelectValue placeholder="Chart type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="pie">
<div className="flex items-center">
<PieChart className="size-4 mr-2 shrink-0" />
<p className="line-clamp-1">
Pie chart
</p>
</div>
</SelectItem>
<SelectItem value="radar">
<div className="flex items-center">
<Radar className="size-4 mr-2 shrink-0" />
<p className="line-clamp-1">
Radar chart
</p>
</div>
</SelectItem>
<SelectItem value="radial">
<div className="flex items-center">
<Target className="size-4 mr-2 shrink-0" />
<p className="line-clamp-1">
Radial chart
</p>
</div>
</SelectItem>
</SelectContent>
</Select>
</CardHeader>
<CardContent>
{data.length === 0 ? (
<div className="felx flex-col gap-y-4 items-center justify-center h-[350px] w-full">
<FileSearch className="size-6 text-muted-foreground" />
<p className="text-muted-foreground text-sm">
No data for this period
</p>
</div>
) : (
<>
{ chartType === "pie" && <PieVariant data={data} /> }
{ chartType === "radar" && <RadarVariant data={data} /> }
{ chartType === "radial" && <RadialVariant data={data} /> }
</>
)}
</CardContent>
</Card>
)
}

export const SpendingPieLoading =() => {
return (
<Card className="border-none drop-shadow-sm">
<CardHeader className="flex space-y-2 lg:space-y-0 lg:flex-row lg:items-center justify-between">
<Skeleton className="h-8 w-48" />
<Skeleton className="h-8 lg:w-[120px] w-full" />
</CardHeader>
<CardContent>
<div className="h-[350px] w-full flex items-center justify-center">
<Loader2 className="h-6 w-6 text-slate-300 animate-spin"/>
</div>
</CardContent>
</Card>
);
};
More...

请我喝杯咖啡吧~

支付宝
微信