1.11 扩展和集成
好的, 让我们来详细规划第 11 部分: 扩展和集成。我们将这部分分为 3 天, 每天专注于特定的扩展和集成任务。
第 11 部分:扩展和集成 (3 天)
Day 1: 开发浏览器扩展
- 设置浏览器扩展项目结构
创建新的目录
browser-extension并初始化:
mkdir browser-extension
cd browser-extension
npm init -y
npm install --save-dev webpack webpack-cli copy-webpack-plugin
- 创建 manifest 文件
在
browser-extension目录下创建manifest.json:
{
"manifest_version": 2,
"name": "LightweightTodo Pro Extension",
"version": "1.0",
"description": "Quickly add todos from any webpage",
"permissions": ["activeTab", "storage"],
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
},
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
}
- 创建 popup 界面
在
browser-extension目录下创建popup.html:
<!DOCTYPE html>
<html>
<head>
<title>Add Todo</title>
</head>
<body>
<form id="addTodoForm">
<input type="text" id="todoTitle" placeholder="Todo title" required>
<textarea id="todoDescription" placeholder="Description"></textarea>
<button type="submit">Add Todo</button>
</form>
<script src="popup.js"></script>
</body>
</html>
- 实现 popup 逻辑
创建
browser-extension/src/popup.ts:
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('addTodoForm') as HTMLFormElement;
const titleInput = document.getElementById('todoTitle') as HTMLInputElement;
const descriptionInput = document.getElementById('todoDescription') as HTMLTextAreaElement;
form.addEventListener('submit', async (e) => {
e.preventDefault();
const title = titleInput.value;
const description = descriptionInput.value;
try {
const response = await fetch('https://api.lightweighttodo.com/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
},
body: JSON.stringify({ title, description })
});
if (response.ok) {
alert('Todo added successfully!');
titleInput.value = '';
descriptionInput.value = '';
} else {
throw new Error('Failed to add todo');
}
} catch (error) {
alert('Error: ' + error.message);
}
});
});
- 配置 webpack
创建
browser-extension/webpack.config.js:
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
entry: './src/popup.ts',
output: {
filename: 'popup.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js'],
},
plugins: [
new CopyPlugin({
patterns: [
{ from: 'manifest.json', to: 'manifest.json' },
{ from: 'popup.html', to: 'popup.html' },
{ from: 'icons', to: 'icons' },
],
}),
],
};
Day 2: 开发公共 API
- 设计 API 端点
创建
api-design.md:
# LightweightTodo Pro Public API
Base URL: https://api.lightweighttodo.com/v1
## Authentication
All requests must include an `Authorization` header with a valid API key.
## Endpoints
### List Todos
GET /todos
Query Parameters:
- status: 'all' | 'active' | 'completed'
- page: number
- limit: number
### Get Todo
GET /todos/:id
### Create Todo
POST /todos
Request Body:
{
"title": string,
"description": string,
"dueDate": string (ISO 8601 format),
"tags": string[]
}
### Update Todo
PUT /todos/:id
Request Body:
{
"title": string,
"description": string,
"dueDate": string (ISO 8601 format),
"tags": string[],
"status": 'active' | 'completed'
}
### Delete Todo
DELETE /todos/:id
### List Tags
GET /tags
### Create Tag
POST /tags
Request Body:
{
"name": string
}
### Delete Tag
DELETE /tags/:id
- 实现 API 端点
在后端项目中创建新的控制器
PublicApiController.ts:
import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards } from '@nestjs/common';
import { ApiKeyGuard } from '../guards/api-key.guard';
import { TodoService } from '../services/todo.service';
import { TagService } from '../services/tag.service';
@Controller('v1')
@UseGuards(ApiKeyGuard)
export class PublicApiController {
constructor(
private readonly todoService: TodoService,
private readonly tagService: TagService
) {}
@Get('todos')
async listTodos(@Query() query) {
return this.todoService.list(query);
}
@Get('todos/:id')
async getTodo(@Param('id') id: string) {
return this.todoService.get(id);
}
@Post('todos')
async createTodo(@Body() todoData) {
return this.todoService.create(todoData);
}
@Put('todos/:id')
async updateTodo(@Param('id') id: string, @Body() todoData) {
return this.todoService.update(id, todoData);
}
@Delete('todos/:id')
async deleteTodo(@Param('id') id: string) {
return this.todoService.delete(id);
}
@Get('tags')
async listTags() {
return this.tagService.list();
}
@Post('tags')
async createTag(@Body() tagData) {
return this.tagService.create(tagData);
}
@Delete('tags/:id')
async deleteTag(@Param('id') id: string) {
return this.tagService.delete(id);
}
}
- 实现 API 密钥认证
创建
src/guards/api-key.guard.ts:
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class ApiKeyGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
const apiKey = request.get('Authorization');
if (!apiKey) {
throw new UnauthorizedException('API key is missing');
}
// In a real application, you would validate the API key against a database
if (apiKey !== 'valid-api-key') {
throw new UnauthorizedException('Invalid API key');
}
return true;
}
}
- 编写 API 文档 使用 Swagger 生成 API 文档。安装所需依赖:
在 main.ts 中设置 Swagger:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('LightweightTodo Pro API')
.setDescription('The LightweightTodo Pro API description')
.setVersion('1.0')
.addApiKey({ type: 'apiKey', name: 'Authorization', in: 'header' }, 'API-Key')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
Day 3: 集成第三方服务
- 集成 Google Calendar 安装所需依赖:
创建 src/services/google-calendar.service.ts:
import { Injectable } from '@nestjs/common';
import { google } from 'googleapis';
@Injectable()
export class GoogleCalendarService {
private calendar;
constructor() {
const auth = new google.auth.JWT(
process.env.GOOGLE_CLIENT_EMAIL,
null,
process.env.GOOGLE_PRIVATE_KEY,
['https://www.googleapis.com/auth/calendar']
);
this.calendar = google.calendar({ version: 'v3', auth });
}
async addEvent(todo) {
const event = {
summary: todo.title,
description: todo.description,
start: {
dateTime: todo.dueDate,
timeZone: 'UTC',
},
end: {
dateTime: todo.dueDate,
timeZone: 'UTC',
},
};
try {
const response = await this.calendar.events.insert({
calendarId: 'primary',
resource: event,
});
return response.data;
} catch (error) {
console.error('Error adding event to Google Calendar:', error);
throw error;
}
}
}
- 集成 Slack 通知 安装所需依赖:
创建 src/services/slack.service.ts:
import { Injectable } from '@nestjs/common';
import { WebClient } from '@slack/web-api';
@Injectable()
export class SlackService {
private slack: WebClient;
constructor() {
this.slack = new WebClient(process.env.SLACK_BOT_TOKEN);
}
async sendNotification(message: string) {
try {
await this.slack.chat.postMessage({
channel: process.env.SLACK_CHANNEL_ID,
text: message,
});
} catch (error) {
console.error('Error sending Slack notification:', error);
throw error;
}
}
}
- 实现集成逻辑
更新
TodoService以使用新的集成服务:
import { Injectable } from '@nestjs/common';
import { GoogleCalendarService } from './google-calendar.service';
import { SlackService } from './slack.service';
@Injectable()
export class TodoService {
constructor(
private readonly googleCalendarService: GoogleCalendarService,
private readonly slackService: SlackService
) {}
async create(todoData) {
// 创建todo的逻辑...
// 添加到Google Calendar
await this.googleCalendarService.addEvent(createdTodo);
// 发送Slack通知
await this.slackService.sendNotification(`New todo created: ${createdTodo.title}`);
return createdTodo;
}
// 其他方法...
}
- 配置环境变量
更新
.env文件:
GOOGLE_CLIENT_EMAIL=your-google-client-email
GOOGLE_PRIVATE_KEY=your-google-private-key
SLACK_BOT_TOKEN=your-slack-bot-token
SLACK_CHANNEL_ID=your-slack-channel-id
- 更新文档
在
README.md中添加新的集成信息:
## Integrations
### Google Calendar
LightweightTodo Pro now integrates with Google Calendar. When you create a todo with a due date, it will automatically be added to your Google Calendar.
### Slack Notifications
Receive Slack notifications for important todo updates. Configure your Slack workspace and channel in the settings to enable this feature.
## API
LightweightTodo Pro now offers a public API for developers. Check out our [API documentation](api-docs.md) for more information on how to integrate LightweightTodo Pro into your own applications.
这个详细计划涵盖了第 11 部分的扩展和集成, 包括开发浏览器扩展、创建公共 API, 以及集成第三方服务如 Google Calendar 和 Slack。通过这些步骤, LightweightTodo Pro 的功能得到了显著扩展, 为用户提供了更多的使用方式和集成选项。