1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

release 0.3.4

This commit is contained in:
songjunxi
2023-11-10 14:59:47 +08:00
parent 2b456637e9
commit 602f2059fd
161 changed files with 9921 additions and 347 deletions

View File

@@ -0,0 +1,106 @@
import NextAuth, { NextAuthOptions, Theme } from "next-auth";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import prisma from "@/lib/db/prisma";
import EmailProvider from "next-auth/providers/email";
import GithubProvider from "next-auth/providers/github";
import GoogleProvider from "next-auth/providers/google";
import { createTransport } from "nodemailer";
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID as string,
clientSecret: process.env.GITHUB_SECRET as string,
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
EmailProvider({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
maxAge: 10 * 60, // 10min, 邮箱链接失效时间默认24小时
async sendVerificationRequest({
identifier: email,
url,
provider,
theme,
}) {
const { host } = new URL(url);
const transport = createTransport(provider.server);
const result = await transport.sendMail({
to: email,
from: provider.from,
subject: `You are logging in to Inke`,
text: text({ url, host }),
html: html({ url, host, theme }),
});
const failed = result.rejected.concat(result.pending).filter(Boolean);
if (failed.length) {
throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`);
}
},
}),
],
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
/**
*使用HTML body 代替正文内容
*/
function html(params: { url: string; host: string; theme: Theme }) {
const { url, host, theme } = params;
const escapedHost = host.replace(/\./g, "​.");
const brandColor = theme.brandColor || "#346df1";
const color = {
background: "#f9f9f9",
text: "#444",
mainBackground: "#fff",
buttonBackground: brandColor,
buttonBorder: brandColor,
buttonText: theme.buttonText || "#fff",
};
return `
<body style="background: ${color.background};">
<table width="100%" border="0" cellspacing="10" cellpadding="0"
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
<tr>
<td align="center"
style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
<strong>Welcome to Inke</strong> 🎉
</td>
</tr>
<tr>
<td align="center" style="padding: 5px 0;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
target="_blank"
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
in now</a></td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="left"
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
Button click without response? Try open this <a href="${url}" target="_blank">link</a> in your browser. If you did not request this email you can safely ignore it.
</td>
</tr>
</table>
</body>
`;
}
/** 不支持HTML 的邮件客户端会显示下面的文本信息 */
function text({ url, host }: { url: string; host: string }) {
return `Welcome to Inke! This is a magic link, click on it to log in ${url}\n`;
}

View File

@@ -0,0 +1,38 @@
import { NextRequest, NextResponse } from "next/server";
import { findCollaborationInviteCount } from "@/lib/db/collaboration";
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id || id === "undefined") {
return NextResponse.json({
code: 403,
msg: "Empty roomId",
data: null,
});
}
const res = await findCollaborationInviteCount(id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Not joined the collaboration space",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,45 @@
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { authOptions } from "../../auth/[...nextauth]/route";
import { getUserByEmail } from "@/lib/db/user";
import {
findCollaborationByDBId,
findCollaborationInviteCount,
} from "@/lib/db/collaboration";
// /invite/:id 邀请页调用,查询此邀请详细信息,不需要登录,点击“加入协作”后才需要鉴权
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({
code: 403,
msg: "Empty roomId",
data: null,
});
}
const res = await findCollaborationByDBId(id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Not joined the collaboration space",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,65 @@
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { authOptions } from "../../auth/[...nextauth]/route";
import { getUserByEmail } from "@/lib/db/user";
import {
findCollaborationByDBId,
findCollaborationBylocalId,
} from "@/lib/db/collaboration";
// /invite/:id 邀请页调用,查询此邀请详细信息,不需要登录,点击“加入协作”后才需要鉴权
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "Unauthorized! Please login",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Something wrong",
data: null,
});
}
const { searchParams } = new URL(req.url);
const id = searchParams.get("localId");
if (!id) {
return NextResponse.json({
code: 403,
msg: "Empty id",
data: null,
});
}
const res = await findCollaborationBylocalId(id, user.id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Not joined collaboration",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,107 @@
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { authOptions } from "../../auth/[...nextauth]/route";
import { getUserByEmail } from "@/lib/db/user";
import { findCollaborationByRoomId } from "@/lib/db/collaboration";
// post页面获取详情时调用需要用户id查询是否已经加入加入才开启协作模式
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "Unauthorized! Please login first",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Not found user",
data: null,
});
}
const { searchParams } = new URL(req.url);
const id = searchParams.get("roomId");
if (!id) {
return NextResponse.json({
code: 403,
msg: "Empty roomId",
data: null,
});
}
// 查询用户是否已经加入了这个协作已经创建了这个room记录
const res = await findCollaborationByRoomId(id, user.id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "You haven't joined this collaboration yet",
data: null,
});
} catch (error) {
return NextResponse.json({
code: 500,
msg: error,
data: null,
});
}
}
export async function POST(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { roomId } = await req.json();
if (!roomId) {
return NextResponse.json({
code: 403,
msg: "Empty roomId",
data: null,
});
}
const res = await findCollaborationByRoomId(roomId);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Not found",
data: null,
});
} catch (error) {
return NextResponse.json({
code: 500,
msg: error,
data: null,
});
}
}
// fix error: "DYNAMIC_SERVER_USAGE"
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,180 @@
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { authOptions } from "../auth/[...nextauth]/route";
import { getUserByEmail } from "@/lib/db/user";
import {
createCollaboration,
deleteCollaborationNote,
findCollaborationByRoomId,
findUserCollaborations,
} from "@/lib/db/collaboration";
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "Unauthorized! Please login",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Something wrong",
data: null,
});
}
const res = await findUserCollaborations(user.id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Not joined collaboration",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
export async function POST(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "Unauthorized! Please login",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Something wrong",
data: null,
});
}
const { localId, roomId, title } = await req.json();
if (!localId || !roomId) {
return NextResponse.json({
code: 405,
msg: "Empty params",
data: null,
});
}
// 判断用户是否已经加入此协作
const find_res = await findCollaborationByRoomId(roomId, user.id);
if (find_res) {
return NextResponse.json({
code: 301,
msg: "Joined! Redirecting...",
data: find_res,
});
}
// TODO: 限制新建个数
// 新建
const res = await createCollaboration(user.id, localId, roomId, title);
if (res) {
return NextResponse.json({
code: 200,
msg: "SuccessfullyRedirecting...",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json({
code: 500,
msg: error,
data: null,
});
}
}
export async function DELETE(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "Unauthorized! Please login",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Something wrong",
data: null,
});
}
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({
code: 403,
msg: "Empty id",
data: null,
});
}
const res = await deleteCollaborationNote(id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
// fix error: "DYNAMIC_SERVER_USAGE"
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,109 @@
import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
import { kv } from "@vercel/kv";
import { Ratelimit } from "@upstash/ratelimit";
import { getRandomElement } from "@/lib/utils";
import { Account_Plans } from "../../../../lib/consts";
const api_key = process.env.OPENAI_API_KEY || "";
const api_keys = process.env.OPENAI_API_KEYs || "";
const openai = new OpenAI({
baseURL: process.env.OPENAI_API_PROXY || "https://api.openai.com",
});
// IMPORTANT! Set the runtime to edge: https://vercel.com/docs/functions/edge-functions/edge-runtime
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
try {
// Check if the OPENAI_API_KEY is set, if not return 400
if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === "") {
return new Response(
"Missing OPENAI_API_KEY  make sure to add it to your .env file.",
{
status: 400,
},
);
}
const { prompt, plan, messages, system } = await req.json();
const planN = Number(plan || "5");
if (
messages &&
messages.length > Account_Plans[planN].ai_bot_history_length
) {
return new Response("You have reached the history message limit.", {
status: 429,
});
}
if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) {
const ip = req.headers.get("x-forwarded-for");
const ratelimit = new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(
Account_Plans[planN].ai_generate_day,
"1 d",
),
});
// console.log("plan", planN, Account_Plans[planN], ip);
const { success, limit, reset, remaining } = await ratelimit.limit(
`novel_ratelimit_${ip}`,
);
if (!success) {
return new Response(
"You have reached your request limit for the day.",
{
status: 429,
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
},
},
);
}
}
openai.apiKey = getRandomElement(api_keys.split(",")) || api_key;
const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo-16k",
messages: [
{
role: "system",
content:
"As a note assistant, communicate with users based on the input note content.",
// "Do not reply to questions unrelated to the notes. If there are questions unrelated to the notes, please reply 'Please ask questions related to the notes'",
},
{
role: "system",
content: `Note content: \n${system}`,
},
...messages,
],
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
stream: true,
n: 1,
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
} catch (error) {
return new Response(`Server error\n${error}`, {
status: 500,
});
}
}

View File

@@ -0,0 +1,99 @@
import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
import { kv } from "@vercel/kv";
import { Ratelimit } from "@upstash/ratelimit";
import { getRandomElement } from "@/lib/utils";
import { Account_Plans } from "../../../../lib/consts";
const api_key = process.env.OPENAI_API_KEY || "";
const api_keys = process.env.OPENAI_API_KEYs || "";
const openai = new OpenAI();
// IMPORTANT! Set the runtime to edge: https://vercel.com/docs/functions/edge-functions/edge-runtime
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
try {
// Check if the OPENAI_API_KEY is set, if not return 400
if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === "") {
return new Response(
"Missing OPENAI_API_KEY  make sure to add it to your .env file.",
{
status: 400,
},
);
}
const { prompt, plan } = await req.json();
const planN = Number(plan || "5");
if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) {
const ip = req.headers.get("x-forwarded-for");
const ratelimit = new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(
Account_Plans[planN].ai_generate_day,
"1 d",
),
});
// console.log("plan", planN, Account_Plans[planN], ip);
const { success, limit, reset, remaining } = await ratelimit.limit(
`novel_ratelimit_${ip}`,
);
if (!success) {
return new Response(
"You have reached your request limit for the day.",
{
status: 429,
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
},
},
);
}
}
openai.apiKey = getRandomElement(api_keys.split(",")) || api_key;
const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo-16k",
messages: [
{
role: "system",
content:
"You are an AI writing assistant that continues existing text based on context from prior text." +
"Give more weight/priority to the later characters than the beginning ones. " +
`Limit your response to no more than ${Account_Plans[planN].ai_generate_chars} characters, but make sure to construct complete sentences.`,
// "Use Markdown formatting when appropriate.",
},
{
role: "user",
content: prompt,
},
],
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
stream: true,
n: 1,
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
} catch (error) {
return new Response("Server error", {
status: 500,
});
}
}

View File

@@ -0,0 +1,101 @@
import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
import { kv } from "@vercel/kv";
import { Ratelimit } from "@upstash/ratelimit";
import { getRandomElement } from "@/lib/utils";
import { Account_Plans } from "../../../../lib/consts";
const api_key = process.env.OPENAI_API_KEY || "";
const api_keys = process.env.OPENAI_API_KEYs || "";
const openai = new OpenAI();
// IMPORTANT! Set the runtime to edge: https://vercel.com/docs/functions/edge-functions/edge-runtime
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
try {
// Check if the OPENAI_API_KEY is set, if not return 400
if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === "") {
return new Response(
"Missing OPENAI_API_KEY  make sure to add it to your .env file.",
{
status: 400,
},
);
}
const { prompt, plan } = await req.json();
const planN = Number(plan || "5");
if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) {
const ip = req.headers.get("x-forwarded-for");
const ratelimit = new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(
Account_Plans[planN].ai_generate_day,
"1 d",
),
});
// console.log("plan", planN, Account_Plans[planN], ip);
const { success, limit, reset, remaining } = await ratelimit.limit(
`novel_ratelimit_${ip}`,
);
if (!success) {
return new Response(
"You have reached your request limit for the day.",
{
status: 429,
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
},
},
);
}
}
openai.apiKey = getRandomElement(api_keys.split(",")) || api_key;
const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo-16k",
messages: [
{
role: "system",
content: `I hope you can take on roles such as spell proofreading and rhetorical improvement,
or other roles related to text editing, optimization, and abbreviation. I will
communicate with you in any language, and you will recognize the language. Please only answer the corrected and improved parts, and
do not write explanations.
Limit your response to no more than ${Account_Plans[planN].ai_generate_chars} characters,
but make sure to construct complete sentences.`,
// "Use Markdown formatting when appropriate.",
},
{
role: "user",
content: prompt,
},
],
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
stream: true,
n: 1,
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
} catch (error) {
return new Response("Server error", {
status: 500,
});
}
}

View File

@@ -1,81 +0,0 @@
import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
import { kv } from "@vercel/kv";
import { Ratelimit } from "@upstash/ratelimit";
// Create an OpenAI API client (that's edge friendly!)
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY || "",
});
// IMPORTANT! Set the runtime to edge: https://vercel.com/docs/functions/edge-functions/edge-runtime
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
// Check if the OPENAI_API_KEY is set, if not return 400
if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === "") {
return new Response(
"Missing OPENAI_API_KEY  make sure to add it to your .env file.",
{
status: 400,
},
);
}
if (
process.env.NODE_ENV != "development" &&
process.env.KV_REST_API_URL &&
process.env.KV_REST_API_TOKEN
) {
const ip = req.headers.get("x-forwarded-for");
const ratelimit = new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(50, "1 d"),
});
const { success, limit, reset, remaining } = await ratelimit.limit(
`novel_ratelimit_${ip}`,
);
if (!success) {
return new Response("You have reached your request limit for the day.", {
status: 429,
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
},
});
}
}
let { prompt } = await req.json();
const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content:
"You are an AI writing assistant that continues existing text based on context from prior text. " +
"Give more weight/priority to the later characters than the beginning ones. " +
"Limit your response to no more than 200 characters, but make sure to construct complete sentences.",
},
{
role: "user",
content: prompt,
},
],
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
stream: true,
n: 1,
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
}

View File

@@ -0,0 +1,96 @@
import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
import { kv } from "@vercel/kv";
import { Ratelimit } from "@upstash/ratelimit";
import { getRandomElement } from "@/lib/utils";
import { Account_Plans } from "../../../../lib/consts";
const api_key = process.env.OPENAI_API_KEY || "";
const api_keys = process.env.OPENAI_API_KEYs || "";
const openai = new OpenAI();
// IMPORTANT! Set the runtime to edge: https://vercel.com/docs/functions/edge-functions/edge-runtime
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
try {
// Check if the OPENAI_API_KEY is set, if not return 400
if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === "") {
return new Response(
"Missing OPENAI_API_KEY  make sure to add it to your .env file.",
{
status: 400,
},
);
}
const { prompt, plan } = await req.json();
const planN = Number(plan || "5");
if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) {
const ip = req.headers.get("x-forwarded-for");
const ratelimit = new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(
Account_Plans[planN].ai_generate_day,
"1 d",
),
});
// console.log("plan", planN, Account_Plans[planN], ip);
const { success, limit, reset, remaining } = await ratelimit.limit(
`novel_ratelimit_${ip}`,
);
if (!success) {
return new Response(
"You have reached your request limit for the day.",
{
status: 429,
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
},
},
);
}
}
openai.apiKey = getRandomElement(api_keys.split(",")) || api_key;
const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo-16k",
messages: [
{
role: "system",
content:
"I hope you can play the role of translator and spell proofreader. I will communicate with you in any language, and you will recognize the language and translate it to answer me.",
},
{
role: "user",
content: prompt,
},
],
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
stream: true,
n: 1,
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
} catch (error) {
return new Response("Server error", {
status: 500,
});
}
}

View File

@@ -0,0 +1,51 @@
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { authOptions } from "../../auth/[...nextauth]/route";
import { getUserByEmail } from "@/lib/db/user";
import { findUserShares } from "@/lib/db/share";
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "UnAuth",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Something wrong",
data: null,
});
}
const res = await findUserShares(user.id);
if (res) {
return NextResponse.json({
code: 200,
msg: "",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,168 @@
import { getServerSession } from "next-auth";
import { NextRequest, NextResponse } from "next/server";
import { authOptions } from "../auth/[...nextauth]/route";
import { getUserByEmail } from "@/lib/db/user";
import {
createShareNote,
deleteShareNote,
findShareByLocalId,
findUserSharesCount,
updateShareClick,
updateShareKeeps,
updateShareNote,
} from "@/lib/db/share";
import { Account_Plans } from "@/lib/consts";
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({
code: 403,
msg: "Empty id",
data: null,
});
}
const res = await findShareByLocalId(id);
if (res) {
await updateShareClick(res.id, res.click); // 数据库id
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
export async function POST(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({
code: 401,
msg: "Unauthorized! Please login",
data: null,
});
}
const user = await getUserByEmail(session.user.email);
if (!user) {
return NextResponse.json({
code: 403,
msg: "Something wrong",
data: null,
});
}
const { data } = await req.json();
if (!data) {
return NextResponse.json({
code: 405,
msg: "Empty data",
data: null,
});
}
const find_share_count = await findUserSharesCount(user.id);
if (
find_share_count >= Account_Plans[Number(user.plan)].note_upload_count
) {
return NextResponse.json({
code: 429,
msg: "You have exceeded the maximum number of uploads, please upgrade your plan.",
data: null,
});
}
// 必需要用户ID
const find_res = await findShareByLocalId(data.id, user.id);
if (find_res) {
const update_res = await updateShareNote(data, find_res.id);
return NextResponse.json({
code: 200,
msg: "Updated!",
data: update_res,
});
}
const res = await createShareNote(data, user.id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json({
code: 500,
msg: error,
data: null,
});
}
}
export async function DELETE(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({
code: 403,
msg: "Empty id",
data: null,
});
}
const res = await deleteShareNote(id);
if (res) {
return NextResponse.json({
code: 200,
msg: "Successed!",
data: res,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json(error);
}
}
// fix error: "DYNAMIC_SERVER_USAGE"
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,45 @@
import { NextRequest, NextResponse } from "next/server";
import { findShareByDBId, updateShareKeeps } from "@/lib/db/share";
export async function POST(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { id } = await req.json();
if (!id) {
return NextResponse.json({
code: 405,
msg: "Empty id",
data: null,
});
}
const find_res = await findShareByDBId(id);
if (find_res) {
const res = await updateShareKeeps(id, find_res.keeps);
return NextResponse.json({
code: 200,
msg: "success",
data: null,
});
}
return NextResponse.json({
code: 404,
msg: "Something wrong",
data: null,
});
} catch (error) {
return NextResponse.json({
code: 500,
msg: error,
data: null,
});
}
}
// fix error: "DYNAMIC_SERVER_USAGE"
export const dynamic = "force-dynamic";

View File

@@ -0,0 +1,9 @@
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({
subject: "website",
status: "live",
color: "green",
});
}

View File

@@ -1,17 +1,46 @@
import { put } from "@vercel/blob";
import { Account_Plans } from "@/lib/consts";
import { NextResponse } from "next/server";
// import { getServerSession } from "next-auth";
// import { authOptions } from "../auth/[...nextauth]/route";
// import { getUserByEmail } from "@/lib/db/user";
import COS from "cos-nodejs-sdk-v5";
import { Readable } from "stream";
export const runtime = "edge";
const uploadFile = (stream: Readable, filename: string): Promise<any> => {
return new Promise((resolve, reject) => {
const params = {
Bucket: "gcloud-1303456836",
Region: "ap-chengdu",
Key: "inke/" + filename,
Body: stream,
};
cos.putObject(params, (err: any, data: any) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};
export const runtime = "nodejs";
var cos = new COS({
SecretId: process.env.TencentSecretID || "",
SecretKey: process.env.TencentSecretKey || "",
});
export async function POST(req: Request) {
if (!process.env.BLOB_READ_WRITE_TOKEN) {
return new Response(
"Missing BLOB_READ_WRITE_TOKEN. Don't forget to add that to your .env file.",
{
status: 401,
},
);
}
// if (!process.env.BLOB_READ_WRITE_TOKEN) {
// return new Response(
// "Missing BLOB_READ_WRITE_TOKEN. Don't forget to add that to your .env file.",
// {
// status: 401,
// },
// );
// }
const file = req.body || "";
const filename = req.headers.get("x-vercel-filename") || "file.txt";
@@ -22,10 +51,25 @@ export async function POST(req: Request) {
const finalName = filename.includes(fileType)
? filename
: `${filename}${fileType}`;
const blob = await put(finalName, file, {
contentType,
access: "public",
});
return NextResponse.json(blob);
const fileStream = Readable.from(file as any);
const res = await uploadFile(fileStream, finalName);
// console.log("上传结果", res, res.Location);
if (res && res.statusCode === 200) {
return NextResponse.json({ url: "https://" + res.Location });
}
// if (
// blob &&
// Number(blob.size) > Account_Plans[plan].image_upload_size * 1024 * 1024
// ) {
// return new Response(
// "You have exceeded the maximum size of uploads, please upgrade your plan.",
// {
// status: 429,
// },
// );
// }
}

View File

@@ -0,0 +1,50 @@
import { NextRequest, NextResponse } from "next/server";
import { getUserByEmail, getUserById, updateUserName } from "@/lib/db/user";
export async function GET(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { searchParams } = new URL(req.url);
const email = searchParams.get("email");
const id = searchParams.get("id");
if (email) {
const user = await getUserByEmail(email);
return NextResponse.json(user);
} else if (id && id !== "undefined") {
const user = await getUserById(id);
return NextResponse.json(user);
}
return NextResponse.json("empty params");
} catch (error) {
return NextResponse.json(error);
}
}
export async function POST(
req: NextRequest,
{ params }: { params: Record<string, string | string | undefined[]> },
) {
try {
const { userId, userName } = await req.json();
if (!userId || !userName) {
return NextResponse.json("empty content");
}
const res = await updateUserName(userId, userName);
if (res) {
return NextResponse.json(res);
}
return NextResponse.json("something wrong");
} catch {
return NextResponse.json("error");
}
}
// fix error: "DYNAMIC_SERVER_USAGE"
export const dynamic = "force-dynamic";