文字直播用 chatgpt 开发一个笔记软件。 希望可以在睡觉前部署上线。

327 天前
 hamsterbase

下面是之前的聊天记录

Q: 我现在希望开发一个卡片笔记软件。UI 界面类似于 imessage 。 请问叫什么名字比较好?

A 为了取一个好名字,需要考虑软件的特点和目标用户。考虑到你的软件类似于 iMessage ,那么你可以考虑使用类似的命名方式。以下是一些可能的建议:

CardMessage NoteChat CardNote SnapNote MemoTalk NoteMessage 在考虑具体的名字时,可以考虑软件的品牌形象,功能特点等方面,以便从众多同类软件中脱颖而出

  1. 搜了一下,memo talk 之前没人开发过,就叫这个名字吧。

  2. 建了一个 GitHub 仓库 https://github.com/hamsterbase/memo-talk

Q: sha256 转化为字符串,长度是多少。

A:

SHA256 是一种加密算法,它生成的哈希值( hash value )通常表示为 64 个字符的十六进制字符串( hexadecimal string ),因此 SHA256 转化为字符串后的长度是 64 个字符。每个字符是 0-9 和 A-F 之间的一个 16 进制数字,因此它们可以表示 256 个可能的值( 16 的 4 次方)。

5

Q:

我希望你能帮我编写一下服务端的代码。

需要有以下四个接口接口为

  1. 获取文件夹列表

  2. 根据文件夹名字,文件名获取文件内容

  3. 根据文件夹名字,文件名删除文件

  4. 根据文件夹名字,文件名删除文件

要求。

  1. 基于 koa 编写
  2. 使用 typescript 编写
  3. 文件名,文件名必须为 64 个字符,每个字符是 0-9 和 A-F 之间的一个 16 进制数字
  4. 文件夹名和文件名,文件内容都通过 body 传递。

A: 以下是一个基于 Koa 和 TypeScript 的简单实现,其中包含了四个接口:

import Koa from "koa";
import bodyParser from "koa-bodyparser";

const app = new Koa();

// 定义文件夹列表
let folders: string[] = [];

// 定义文件内容
let files: { [key: string]: string } = {};

// 获取文件夹列表
app.use(async (ctx, next) => {
  if (ctx.path === "/folders" && ctx.method === "GET") {
    ctx.body = folders;
    ctx.status = 200;
  } else {
    await next();
  }
});

// 获取文件内容
app.use(async (ctx, next) => {
  if (ctx.path === "/files" && ctx.method === "GET") {
    const folderName = ctx.request.body.folderName;
    const fileName = ctx.request.body.fileName;
    const filePath = `${folderName}/${fileName}`;
    if (files[filePath]) {
      ctx.body = files[filePath];
      ctx.status = 200;
    } else {
      ctx.body = "File not found";
      ctx.status = 404;
    }
  } else {
    await next();
  }
});

// 添加文件
app.use(bodyParser());
app.use(async (ctx, next) => {
  if (ctx.path === "/files" && ctx.method === "POST") {
    const folderName = ctx.request.body.folderName;
    const fileName = ctx.request.body.fileName;
    const fileContent = ctx.request.body.fileContent;
    const filePath = `${folderName}/${fileName}`;
    if (files[filePath]) {
      ctx.body = "File already exists";
      ctx.status = 409;
    } else {
      files[filePath] = fileContent;
      ctx.status = 201;
    }
  } else {
    await next();
  }
});

// 删除文件
app.use(async (ctx, next) => {
  if (ctx.path === "/files" && ctx.method === "DELETE") {
    const folderName = ctx.request.body.folderName;
    const fileName = ctx.request.body.fileName;
    const filePath = `${folderName}/${fileName}`;
    if (files[filePath]) {
      delete files[filePath];
      ctx.status = 204;
    } else {
      ctx.body = "File not found";
      ctx.status = 404;
    }
  } else {
    await next();
  }
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

请注意,此代码仅供参考,可能需要根据具体需求进行修改和调整。

3603 次点击
所在节点    分享创造
62 条回复
hamsterbase
327 天前
6. 他没把文件写到本地,所以改一下 prompt

Q:

我希望你能帮我编写一下服务端的代码。

需要有以下四个接口接口为

1. 获取文件夹列表

2. 根据文件夹名字,文件名获取文件内容

3. 根据文件夹名字,文件名删除文件

4. 根据文件夹名字,文件名删除文件

要求。

1. 基于 koa 编写
2. 使用 typescript 编写
3. 文件名,文件名必须为 64 个字符,每个字符是 0-9 和 A-F 之间的一个 16 进制数字
4. 文件夹名和文件名,文件内容都通过 body 传递。
5. 我希望你能用 koa-router 编写
6. 文件希望写入到本地的 data 文件夹下
7. API 必须以 public/api/v1 开头

Q:

```ts
import Koa from "koa";
import Router from "koa-router";
import fs from "fs/promises";
import path from "path";

const app = new Koa();
const router = new Router();

const DATA_FOLDER = "data";

// 获取文件夹列表
router.get("/public/api/v1/folders", async (ctx) => {
try {
const folders = await fs.readdir(DATA_FOLDER);
ctx.body = folders;
} catch (err) {
ctx.status = 500;
ctx.body = "Internal Server Error";
console.error(err);
}
});

// 根据文件夹名字,文件名获取文件内容
router.get(
"/public/api/v1/folders/:folderName/files/:fileName",
async (ctx) => {
const { folderName, fileName } = ctx.params;
const filePath = path.join(DATA_FOLDER, folderName, fileName);
try {
const fileContent = await fs.readFile(filePath, "utf-8");
ctx.body = fileContent;
} catch (err) {
if (err.code === "ENOENT") {
ctx.status = 404;
ctx.body = "File Not Found";
} else {
ctx.status = 500;
ctx.body = "Internal Server Error";
console.error(err);
}
}
}
);

// 根据文件夹名字,文件名删除文件
router.del(
"/public/api/v1/folders/:folderName/files/:fileName",
async (ctx) => {
const { folderName, fileName } = ctx.params;
const filePath = path.join(DATA_FOLDER, folderName, fileName);
try {
await fs.unlink(filePath);
ctx.body = "File Deleted";
} catch (err) {
if (err.code === "ENOENT") {
ctx.status = 404;
ctx.body = "File Not Found";
} else {
ctx.status = 500;
ctx.body = "Internal Server Error";
console.error(err);
}
}
}
);

// 根据文件夹名字,文件名删除文件
router.post(
"/public/api/v1/folders/:folderName/files/:fileName",
async (ctx) => {
const { folderName, fileName } = ctx.params;
const filePath = path.join(DATA_FOLDER, folderName, fileName);
try {
await fs.mkdir(path.join(DATA_FOLDER, folderName), { recursive: true });
await fs.writeFile(filePath, ctx.request.body);
ctx.body = "File Created";
} catch (err) {
ctx.status = 500;
ctx.body = "Internal Server Error";
console.error(err);
}
}
);

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000, () => {
console.log("Server listening on port 3000");
});
```

接口说明:

获取文件夹列表:GET /public/api/v1/folders
根据文件夹名字,文件名获取文件内容:GET /public/api/v1/folders/:folderName/files/:fileName
根据文件夹名字,文件名删除文件:DELETE /public/api/v1/folders/:folderName/files/:fileName
根据文件夹名字,文件名删除文件:POST /public/api/v1/folders/:folderName/files/:fileName
其中,文件名和文件夹名都是 64 个字符的 16 进制数字字符串。文件内容通过 Request Body 传递,文件保存在本地的 data 文件夹下。如果出现错误,会返回适当的 HTTP 状态码和错误信息。
hamsterbase
327 天前
7. 之后不贴 chatgpt 的回答了,复制粘贴太麻烦啦。

Q:

我希望你能帮我编写一下服务端的代码。

参数命名规则为 folder , file , content 。

需要有以下 4 个接口为。

1. 获取 folder 获取文件列表。

2. 根据 folder ,file 获取 content

3. 根据 folder ,file 删除文件

4. 根据 folder ,file, content 写入文件

要求。

1. 基于 koa 编写,使用 typescript 编写
2. folder , file 必须为 64 个字符,每个字符是 0-9 和 A-F 之间的一个 16 进制数字.
3. 我希望你能用 koa-router 编写
4. 文件希望写入到本地的 data 文件夹下
5. 规则为 /data/folder/file
6. API 必须以 public/api/v1 开头
7. content 限制最大 1mb
8. folder, file, content 都通过 body 获取,不要用 query,path 获取。
hamsterbase
327 天前
8. koa-router 好像没有类型,换 @koa/router. 初始化项目,把 chatgpt 回答拷贝一下

https://github.com/hamsterbase/memo-talk/commit/2cc9318a7255e2dafef2716e78b2e844953042cc
hamsterbase
327 天前
9 。chatgpt 没有写文件名校验逻辑,让他单独写一个
帮我用 ts 写一个字符串检测函数。 字符串长度必须为 64 个字符,每个字符是 0-9 和 A-F 之间的一个 16 进制数字.
忽略大小写。 最终输出转换为小写后的结果。 如果不满足,则抛错。
hamsterbase
327 天前
10. 白嫖 render.com , 服务器上线了 https://memo-talk.onrender.com
hamsterbase
327 天前
11. 帮我想一下 memo-talk 前端仓库的名字

以下是一些可能的 Memo-Talk 前端仓库的名字:

- memo-talk-frontend:一个简单而明确的仓库名称,指示该仓库包含 Memo-Talk 的前端代码。
- memo-talk-web:一个更简洁的仓库名称,指示该仓库包含 Memo-Talk 的 Web 界面代码。
- memo-talk-ui:一个仓库名称,指示该仓库包含 Memo-Talk 的用户界面部分。
- memo-talk-client:一个仓库名称,指示该仓库包含 Memo-Talk 的客户端代码。
- memo-talk-portal:一个仓库名称,指示该仓库包含 Memo-Talk 的 Web 门户代码。
你可以根据你的喜好和项目的具体需求选择其中任何一个名称。无论你选择哪个名称,都需要确保它能够准确地描述该仓库的内容,并且易于理解和记忆。此外,你可能还需要考虑使用标准的命名约定和格式,以便其他开发人员能够轻松地找到和使用你的仓库。
hamsterbase
327 天前
hamsterbase
327 天前
13. 使用 vite 的 ts + react 模板,数据核心准备使用 yjs
hamsterbase
327 天前
14. 前端上线啦,可以访问了。 开始写前端逻辑

https://memo-talk-web.onrender.com/
hamsterbase
327 天前
15.

Q: 帮我设计一个 ts interface. NoteCore

1. 新建笔记,返回 ID
2. 根据 ID 返回笔记内容。
3. 获取笔记列表.
4. 根据 ID 删除笔记
hamsterbase
327 天前
16. 设计一下核心的数据结构。 这个是我自己设计的


export interface MemoTalk {
id: string;
content: string;
createTime: number;
}

/**
* MemoTalkCore 核心为一个 Y.Doc 对象
* 保存的时候 encode 为字符串
* 可以合并别的 MemoTalkCore, 入参为字符串
*/
export interface MemoTalkCore {
createMemoTalk(content: string): string;

getMemoTalkById(id: string): MemoTalk;

getMemoTalkList(): MemoTalk[];

deleteMemoTalkById(id: string): void;

/**
* 把整个数据库序列化为字符串
*/
encode(): string;

merge(data: string): void;
}
hamsterbase
327 天前
17. 在 copilot 的帮助下,完成了核心逻辑的实现


const enum YDocKey {
/**
* 避免 id 重复
*/
id = "id",
memoTalks = "memoTalks",
}

export class MemoTalkCore implements IMemoTalkCore {
private ydoc: Y.Doc;
constructor() {
this.ydoc = new Y.Doc();
}

createMemoTalk(content: string): string {
if (typeof content !== "string") {
throw new Error("content must be string");
}
const idMap = this.ydoc.getMap(YDocKey.id);
let id: string = nanoid();
while (idMap.has(id)) {
id = nanoid();
}
const memoTalksArray = this.ydoc.getArray<string>(YDocKey.memoTalks);

const memoTalk = this.ydoc.getMap(id);
memoTalk.set("content", content ?? "");
memoTalk.set("createTime", Date.now());
memoTalksArray.push([id]);
return id;
}

getMemoTalkById(id: string): MemoTalk {
const memoTalk = this.ydoc.getMap(id);
return {
id,
content: memoTalk.get("content") as string,
createTime: memoTalk.get("createTime") as number,
};
}

getMemoTalkList(): MemoTalk[] {
const memoTalksArray = this.ydoc.getArray<string>(YDocKey.memoTalks);
return memoTalksArray.map((id) => this.getMemoTalkById(id));
}

deleteMemoTalkById(id: string): void {
const memoTalksArray = this.ydoc.getArray<string>(YDocKey.memoTalks);
memoTalksArray.delete(memoTalksArray.toArray().indexOf(id));
}

encode(): string {
return fromUint8Array(Y.encodeStateAsUpdate(this.ydoc));
}

merge(data: string): void {
Y.applyUpdate(this.ydoc, toUint8Array(data));
}
}
hamsterbase
327 天前
18 。 帮我为 IMemoTalkCore 编写集成单元测试。 使用 jest


```ts
/**
* MemoTalkCore 核心为一个 Y.Doc 对象
* 保存的时候 encode 为字符串
* 可以合并别的 MemoTalkCore, 入参为字符串
*/
export interface IMemoTalkCore {
createMemoTalk(content: string): string;

getMemoTalkById(id: string): MemoTalk;

getMemoTalkList(): MemoTalk[];

deleteMemoTalkById(id: string): void;

/**
* 把整个数据库序列化为字符串
*/
encode(): string;

merge(data: string): void;
}
```
hamsterbase
327 天前
19. 在 chatgpt 的帮助下测出了一个 bug, 如果删除了,再次获取的时候应该返回 null 。 所有测试都通过啦
hamsterbase
327 天前
20. 完善一下单元测试

Q: 帮我用 jest 编写测试。

如果重复删除,应该抛错
hamsterbase
327 天前
21 Q: 帮我写一个 react 的页面,需要用 typescript 开发。


最顶上是一个提示框。 红色的字 “此项目还在开发中,请不要使用”

中间为一个消息窗口。 消息都靠右。

消息为圆角。消息的背景为蓝色。 参考 imessage

页面背景为白色。

最下面是一个 input 框。 里面有一个按钮。 按钮上面的字为 “发送”


颜色要好看一点。 圆角保存统一。

props 为 memotalks:MemoTalk, onCreateMemoTalk(content:string):void

export interface MemoTalk {
id: string;
content: string;
createTime: number;
}
hamsterbase
327 天前
hamsterbase
327 天前
23: 前端最基础的功能开发玩了,还没接入服务器。


1. 支持新增消息了, 消息更新后会储存到 local storage 里。
2. 刷新页面后支持恢复消息

在线地址: https://memo-talk-web.onrender.com/

明天晚上继续开始直播。

1. 接入服务
2. 支持设置密码和账户
3. 支持下载当前数据库,支持上传数据库。
findex
327 天前
关注一下
ShuaiYH
327 天前
@hamsterbase #5 来试试 zeabur.com 哈哈

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/947094

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX