初始化
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { config } from "~config"
|
||||
import { createHttp } from "./request"
|
||||
|
||||
/** Python AI 后端接口 */
|
||||
const http = createHttp(config.aiBaseApi)
|
||||
|
||||
/** 健康检查 */
|
||||
export function healthCheck() {
|
||||
return http.get('/health/')
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { config } from "~config"
|
||||
import { createHttp } from "./request"
|
||||
|
||||
/** Java 后端接口 */
|
||||
const http = createHttp(config.dataBaseApi)
|
||||
|
||||
/** 根据岗位来源地址查询岗位信息 */
|
||||
export function findJobBySourceUrl(sourceUrl: string) {
|
||||
return http.get('/job/findByUrl', { params: { sourceUrl } })
|
||||
}
|
||||
|
||||
/** 校验登录状态 */
|
||||
export function checkLogin() {
|
||||
return http.get('/public/checkLogin')
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { getCookieValue } from "~utils/cookie"
|
||||
|
||||
interface ApiResult<T = unknown> {
|
||||
code: string | number
|
||||
msg: string
|
||||
data: T
|
||||
timestamp: string
|
||||
uuid: string
|
||||
}
|
||||
|
||||
interface RequestOptions {
|
||||
/** 路径参数,替换 url 中的 :key 占位符 */
|
||||
pathParams?: Record<string, string | number>
|
||||
/** query 参数,拼接到 url 后面 */
|
||||
params?: Record<string, string>
|
||||
/** 请求体 */
|
||||
body?: unknown
|
||||
/** 自定义请求头 */
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
|
||||
/** 替换路径参数,如 /user/:id → /user/123 */
|
||||
function resolvePath(url: string, pathParams?: Record<string, string | number>): string {
|
||||
if (!pathParams) return url
|
||||
return Object.entries(pathParams).reduce(
|
||||
(path, [key, value]) => path.replace(`:${key}`, String(value)),
|
||||
url
|
||||
)
|
||||
}
|
||||
|
||||
async function request<T>(baseUrl: string, url: string, method: string, options: RequestOptions = {}): Promise<T> {
|
||||
const token = await getCookieValue('Token')
|
||||
const headers: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Token: token } : {}),
|
||||
...options.headers,
|
||||
}
|
||||
|
||||
const resolvedPath = resolvePath(url, options.pathParams)
|
||||
const fullUrl = options.params
|
||||
? `${baseUrl}${resolvedPath}?${new URLSearchParams(options.params)}`
|
||||
: `${baseUrl}${resolvedPath}`
|
||||
|
||||
const response = await fetch(fullUrl, {
|
||||
method,
|
||||
headers,
|
||||
body: options.body ? JSON.stringify(options.body) : undefined,
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`)
|
||||
}
|
||||
|
||||
const result: ApiResult<T> = await response.json()
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (result.code != 0) throw new Error(result.msg || '请求失败')
|
||||
return result.data
|
||||
}
|
||||
|
||||
function createHttp(baseUrl: string) {
|
||||
return {
|
||||
get: <T>(url: string, options?: RequestOptions) =>
|
||||
request<T>(baseUrl, url, 'GET', options),
|
||||
post: <T>(url: string, options?: RequestOptions) =>
|
||||
request<T>(baseUrl, url, 'POST', options),
|
||||
put: <T>(url: string, options?: RequestOptions) =>
|
||||
request<T>(baseUrl, url, 'PUT', options),
|
||||
delete: <T>(url: string, options?: RequestOptions) =>
|
||||
request<T>(baseUrl, url, 'DELETE', options),
|
||||
}
|
||||
}
|
||||
|
||||
export { createHttp }
|
||||
+34
-11
@@ -1,19 +1,42 @@
|
||||
/**
|
||||
* Background Service Worker
|
||||
* 插件的后台服务,负责:
|
||||
* 1. 监听插件图标点击事件,通知 Content Script 切换侧边栏
|
||||
* 2. 后续可扩展:定时任务调度、跨页面状态管理、与后端的长连接等
|
||||
*/
|
||||
import { config } from "~config"
|
||||
import { MSG_TYPES } from "~constants"
|
||||
|
||||
export {}
|
||||
|
||||
/**
|
||||
* 监听插件图标点击事件
|
||||
* 由于 manifest 中 action 没有设置 default_popup,
|
||||
* 点击图标会触发此事件,向当前活动标签页发送切换侧边栏的消息
|
||||
*/
|
||||
/** 监听插件图标点击,切换侧边栏 */
|
||||
chrome.action.onClicked.addListener(async (tab) => {
|
||||
if (tab.id) {
|
||||
chrome.tabs.sendMessage(tab.id, { type: "TOGGLE_SIDEBAR" })
|
||||
}
|
||||
})
|
||||
|
||||
/** 监听消息:处理 Cookie 读取请求 */
|
||||
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
// 获取所有 Cookie
|
||||
if (message.type === MSG_TYPES.GET_COOKIES) {
|
||||
const targetUrl = message.url || config.cookieSourceUrl
|
||||
const { hostname } = new URL(targetUrl)
|
||||
chrome.cookies.getAll({ domain: hostname, url: targetUrl }).then((cookies) => {
|
||||
sendResponse({ cookies })
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取单个 Cookie 值
|
||||
if (message.type === MSG_TYPES.GET_TOKEN) {
|
||||
const targetUrl = message.url || config.cookieSourceUrl
|
||||
chrome.cookies.get({ url: targetUrl, name: message.name }).then((cookie) => {
|
||||
sendResponse({ value: cookie?.value || null })
|
||||
})
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
/** 插件安装事件 */
|
||||
chrome.runtime.onInstalled.addListener((details) => {
|
||||
if (details.reason === "install") {
|
||||
console.log("[OfferPie] 插件安装成功")
|
||||
} else if (details.reason === "update") {
|
||||
console.log("[OfferPie] 插件已更新")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 环境配置
|
||||
*/
|
||||
|
||||
/** 当前环境,手动切换 */
|
||||
const ENV = 'dev'
|
||||
|
||||
/** 各环境配置 */
|
||||
const envConfigs: Record<string, {
|
||||
dataBaseApi: string
|
||||
aiBaseApi: string
|
||||
cookieSourceUrl: string
|
||||
}> = {
|
||||
dev: {
|
||||
dataBaseApi: 'http://localhost:8080/api',
|
||||
aiBaseApi: 'http://localhost:8000',
|
||||
cookieSourceUrl: 'http://192.168.31.135:5173',
|
||||
},
|
||||
prod: {
|
||||
dataBaseApi: 'https://your-domain.com/api',
|
||||
aiBaseApi: 'https://your-domain.com/ai',
|
||||
cookieSourceUrl: 'https://your-domain.com',
|
||||
},
|
||||
}
|
||||
|
||||
/** 根据当前环境返回配置 */
|
||||
export function getConfig() {
|
||||
return envConfigs[ENV] || envConfigs.dev
|
||||
}
|
||||
|
||||
export const config = getConfig()
|
||||
@@ -0,0 +1,18 @@
|
||||
/** 存储 key 定义 */
|
||||
export const STORAGE_KEYS = {
|
||||
/** 登录状态 */
|
||||
LOGIN_STATUS: 'loginStatus',
|
||||
/** 上次登录检查时间戳 */
|
||||
LAST_LOGIN_CHECK: 'lastLoginCheck',
|
||||
}
|
||||
|
||||
/** 登录状态缓存有效期(毫秒) */
|
||||
export const LOGIN_CHECK_INTERVAL = 5 * 60 * 1000
|
||||
|
||||
/** 消息事件类型 */
|
||||
export const MSG_TYPES = {
|
||||
/** 获取 Token */
|
||||
GET_TOKEN: 'GET_TOKEN',
|
||||
/** 获取 Cookie */
|
||||
GET_COOKIES: 'GET_COOKIES',
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* 后端 API 通信模块
|
||||
* 封装了与 Java 后端和 Python AI 后端的 REST API 请求方法
|
||||
* 注意:项目有两个后端,新增接口时务必确认是哪个后端的
|
||||
*/
|
||||
|
||||
/** Java 后端基础地址 */
|
||||
const BASE_URL = "http://localhost:8080/api"
|
||||
|
||||
/** Python AI 后端基础地址 */
|
||||
const AI_BASE_URL = "http://localhost:5000/api"
|
||||
|
||||
/** 请求配置选项 */
|
||||
interface ApiOptions {
|
||||
/** 请求方法,默认 GET */
|
||||
method?: string
|
||||
/** 请求体数据 */
|
||||
body?: unknown
|
||||
/** 自定义请求头 */
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
* @param baseUrl - 后端基础地址
|
||||
* @param path - 接口路径,如 /user/info
|
||||
* @param options - 请求配置
|
||||
* @returns 解析后的 JSON 响应数据
|
||||
* @throws 当响应状态码非 2xx 时抛出错误
|
||||
*/
|
||||
async function request<T>(baseUrl: string, path: string, options: ApiOptions = {}): Promise<T> {
|
||||
const { method = "GET", body, headers = {} } = options
|
||||
|
||||
const res = await fetch(`${baseUrl}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...headers
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`API Error: ${res.status} ${res.statusText}`)
|
||||
}
|
||||
|
||||
return res.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* Java 后端接口
|
||||
* 用于用户管理、简历数据、投递记录等业务接口
|
||||
*/
|
||||
export const javaApi = {
|
||||
/** 发送 GET 请求到 Java 后端 */
|
||||
get: <T>(path: string) => request<T>(BASE_URL, path),
|
||||
/** 发送 POST 请求到 Java 后端 */
|
||||
post: <T>(path: string, body: unknown) => request<T>(BASE_URL, path, { method: "POST", body })
|
||||
}
|
||||
|
||||
/**
|
||||
* Python AI 后端接口
|
||||
* 用于页面结构分析、智能填表、简历优化等 AI 功能接口
|
||||
*/
|
||||
export const aiApi = {
|
||||
/** 发送 GET 请求到 Python AI 后端 */
|
||||
get: <T>(path: string) => request<T>(AI_BASE_URL, path),
|
||||
/** 发送 POST 请求到 Python AI 后端 */
|
||||
post: <T>(path: string, body: unknown) => request<T>(AI_BASE_URL, path, { method: "POST", body })
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { get, set } from "~utils/storage"
|
||||
import { checkLogin } from "~api/dataApi"
|
||||
import { STORAGE_KEYS, LOGIN_CHECK_INTERVAL } from "~constants"
|
||||
|
||||
/**
|
||||
* 检查登录状态(带缓存)
|
||||
* 成功后缓存结果,有效期内不重复请求
|
||||
* 失败则清除缓存,下次必定重新检查
|
||||
*/
|
||||
export async function ensureLogin(): Promise<boolean> {
|
||||
const lastCheck = await get<number>(STORAGE_KEYS.LAST_LOGIN_CHECK)
|
||||
|
||||
if (lastCheck && Date.now() - lastCheck < LOGIN_CHECK_INTERVAL) {
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
await checkLogin()
|
||||
await set(STORAGE_KEYS.LAST_LOGIN_CHECK, Date.now())
|
||||
return true
|
||||
} catch {
|
||||
await set(STORAGE_KEYS.LAST_LOGIN_CHECK, 0)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MSG_TYPES } from "~constants"
|
||||
|
||||
/** 获取指定地址下的所有 Cookie,默认取 cookieSourceUrl */
|
||||
export async function getCookies(url?: string) {
|
||||
const res = await chrome.runtime.sendMessage({ type: MSG_TYPES.GET_COOKIES, url })
|
||||
return res?.cookies || []
|
||||
}
|
||||
|
||||
/** 获取指定地址下某个 Cookie 的值,默认取 cookieSourceUrl */
|
||||
export async function getCookieValue(name: string, url?: string) {
|
||||
const res = await chrome.runtime.sendMessage({ type: MSG_TYPES.GET_TOKEN, name, url })
|
||||
return res?.value || null
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Chrome Storage 封装
|
||||
* 统一使用 chrome.storage.local 进行数据持久化
|
||||
*/
|
||||
|
||||
/** 读取存储值 */
|
||||
export async function get<T = unknown>(key: string): Promise<T | null> {
|
||||
const result = await chrome.storage.local.get(key)
|
||||
return (result[key] as T) ?? null
|
||||
}
|
||||
|
||||
/** 写入存储值 */
|
||||
export async function set(key: string, value: unknown): Promise<void> {
|
||||
await chrome.storage.local.set({ [key]: value })
|
||||
}
|
||||
|
||||
/** 删除存储值 */
|
||||
export async function remove(key: string): Promise<void> {
|
||||
await chrome.storage.local.remove(key)
|
||||
}
|
||||
Reference in New Issue
Block a user