Skip to content

Commit

Permalink
fix: improve mutli user logic #289
Browse files Browse the repository at this point in the history
  • Loading branch information
blinko-space committed Dec 12, 2024
1 parent c8dd4fc commit 0aec69e
Show file tree
Hide file tree
Showing 17 changed files with 146 additions and 110 deletions.
5 changes: 5 additions & 0 deletions prisma/migrations/20241212033352_0_23_3/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "tag" ADD COLUMN "accountId" INTEGER;

-- AddForeignKey
ALTER TABLE "tag" ADD CONSTRAINT "tag_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "accounts"("id") ON DELETE SET NULL ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ model accounts {
updatedAt DateTime @updatedAt @db.Timestamptz(6)
notes notes[]
configs config[]
tags tag[]
}

model attachments {
Expand Down Expand Up @@ -75,9 +76,11 @@ model tag {
name String @default("") @db.VarChar
icon String @default("") @db.VarChar
parent Int @default(0)
accountId Int?
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
tagsToNote tagsToNote[]
account accounts? @relation(fields: [accountId], references: [id])
}

model tagsToNote {
Expand Down
18 changes: 5 additions & 13 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,19 +277,11 @@ async function main() {
}
})

//v0.12.11 config add user columns
// const configs = await prisma.config.findMany();
// const admin = await prisma.accounts.findFirst({ where: { role: 'superadmin' } })
// if (admin) {
// for (const config of configs) {
// if (!config.userId) {
// await prisma.config.update({
// where: { id: config.id },
// data: { userId: admin.id }
// });
// }
// }
// }
//v0.23.3
const tagsWithoutAccount = await prisma.tag.findMany({ where: { accountId: null } })
for (const tag of tagsWithoutAccount) {
await prisma.tag.update({ where: { id: tag.id }, data: { accountId: accounts[0]?.id } })
}
}

main()
Expand Down
36 changes: 19 additions & 17 deletions src/components/BlinkoSettings/BasicSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,24 +176,26 @@ export const BasicSetting = observer(() => {
user.canRegister.call()
}}
/>} />
{
user.role == 'superadmin' &&
<Item
leftContent={<>Webhook</>}
rightContent={<>
<Input
placeholder="Enter webhook URL"
value={store.webhookEndpoint}
onChange={(e) => store.webhookEndpoint = e.target.value}
onBlur={async () => {
await PromiseCall(api.config.update.mutate({
key: 'webhookEndpoint',
value: store.webhookEndpoint
}))
}}
className="w-[150px] md:w-[300px]"
/>
</>} />

<Item
leftContent={<>Webhook</>}
rightContent={<>
<Input
placeholder="Enter webhook URL"
value={store.webhookEndpoint}
onChange={(e) => store.webhookEndpoint = e.target.value}
onBlur={async () => {
await PromiseCall(api.config.update.mutate({
key: 'webhookEndpoint',
value: store.webhookEndpoint
}))
}}
className="w-[150px] md:w-[300px]"
/>
</>} />

}
<Item
leftContent={<>{t('two-factor-authentication')}</>}
rightContent={
Expand Down
12 changes: 10 additions & 2 deletions src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,16 @@ export default NextAuth({
}
const user = correctUsers![0]!;

const config = await getGlobalConfig({ id: user.id.toString() })

const config = await getGlobalConfig({
ctx: {
id: user.id.toString(),
role: user.role as 'superadmin' | 'user',
name: user.name,
sub: user.id.toString(),
exp: 0,
iat: 0
}
})
// 2fa verification
if (credentials?.isSecondStep === 'true') {

Expand Down
2 changes: 1 addition & 1 deletion src/pages/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export default function Component() {
return RootStore.Get(ToastPlugin).error(t('the-two-passwords-are-inconsistent'))
}
try {
await api.users.createAdmin.mutate({ name: user, password })
await api.users.register.mutate({ name: user, password })
RootStore.Get(ToastPlugin).success(t('create-successfully-is-about-to-jump-to-the-login'))
setTimeout(() => {
router.push('/signin')
Expand Down
7 changes: 5 additions & 2 deletions src/server/plugins/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { FaissStore } from '@langchain/community/vectorstores/faiss';
import { BaseDocumentLoader } from '@langchain/core/document_loaders/base';
import { FileService } from './files';
import { AiPrompt } from './ai/aiPrompt';
import { Context } from '../context';

//https://js.langchain.com/docs/introduction/
//https://smith.langchain.com/onboarding
Expand Down Expand Up @@ -303,7 +304,7 @@ export class AiService {
return conversationMessage
}

static async enhanceQuery({ query }: { query: string }) {
static async enhanceQuery({ query, ctx }: { query: string, ctx: Context }) {
const { VectorStore } = await AiModelFactory.GetProvider()
const config = await AiModelFactory.globalConfig()
const results = await VectorStore.similaritySearchWithScore(query, 20);
Expand All @@ -319,6 +320,7 @@ export class AiService {
const notes = await prisma.notes.findMany({
where: {
id: { in: filteredResultsWithScore.map(i => i.doc.metadata?.noteId).filter(i => !!i) },
accountId: Number(ctx.id)
},
include: { tags: { include: { tag: true } }, attachments: true }
})
Expand All @@ -331,7 +333,7 @@ export class AiService {
return sortedNotes;
}

static async completions({ question, conversations }: { question: string, conversations: { role: string, content: string }[] }) {
static async completions({ question, conversations, ctx }: { question: string, conversations: { role: string, content: string }[], ctx: Context }) {
try {
const { LLM } = await AiModelFactory.GetProvider()
let searchRes = await AiService.similaritySearch({ question })
Expand All @@ -340,6 +342,7 @@ export class AiService {
if (searchRes && searchRes.length != 0) {
notes = await prisma.notes.findMany({
where: {
accountId: Number(ctx.id),
id: {
in: _.uniqWith(searchRes.map(i => i.metadata?.noteId)).filter(i => !!i) as number[]
}
Expand Down
4 changes: 2 additions & 2 deletions src/server/plugins/ai/aiModelFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { OllamaModelProvider } from "./ollamaModelProvider"
export class AiModelFactory {
static async globalConfig() {
return cache.wrap('globalConfig', async () => {
return await getGlobalConfig()
return await getGlobalConfig({ useAdmin: true })
}, { ttl: 1000 })
}

Expand All @@ -34,7 +34,7 @@ export class AiModelFactory {
TokenTextSplitter: provider.TokenTextSplitter()
}
}

if (globalConfig.aiModelProvider == 'Ollama') {
const provider = new OllamaModelProvider({ globalConfig })
return {
Expand Down
10 changes: 5 additions & 5 deletions src/server/plugins/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { existsSync } from "fs";

export class FileService {
public static async getS3Client() {
const config = await getGlobalConfig();
const config = await getGlobalConfig({ useAdmin: true });
return cache.wrap(`${config.s3Endpoint}-${config.s3Region}-${config.s3Bucket}-${config.s3AccessKeyId}-${config.s3AccessKeySecret}`, async () => {
const s3ClientInstance = new S3Client({
endpoint: config.s3Endpoint,
Expand Down Expand Up @@ -70,8 +70,8 @@ export class FileService {
const extension = path.extname(originalName);
const baseName = path.basename(originalName, extension);
const timestamp = Date.now();
const config = await getGlobalConfig();

const config = await getGlobalConfig({ useAdmin: true });
if (config.objectStorage === 's3') {
const { s3ClientInstance } = await this.getS3Client();

Expand Down Expand Up @@ -100,7 +100,7 @@ export class FileService {
}

static async deleteFile(api_attachment_path: string) {
const config = await getGlobalConfig();
const config = await getGlobalConfig({ useAdmin: true });
if (api_attachment_path.includes('/api/s3file/')) {
const { s3ClientInstance } = await this.getS3Client();
const fileName = api_attachment_path.replace('/api/s3file/', "");
Expand Down Expand Up @@ -130,7 +130,7 @@ export class FileService {
}

static async getFile(filePath: string) {
const config = await getGlobalConfig();
const config = await getGlobalConfig({ useAdmin: true });
const fileName = filePath.replace('/api/file/', '').replace('/api/s3file/', '');
const tempPath = path.join(UPLOAD_FILE_PATH, path.basename(fileName));

Expand Down
4 changes: 2 additions & 2 deletions src/server/routers/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export const aiRouter = router({
question: z.string(),
conversations: z.array(z.object({ role: z.string(), content: z.string() }))
}))
.mutation(async function* ({ input }) {
.mutation(async function* ({ input, ctx }) {
try {
const { question, conversations } = input
const { result: responseStream, notes } = await AiService.completions({ question, conversations })
const { result: responseStream, notes } = await AiService.completions({ question, conversations, ctx })
yield { notes }
for await (const chunk of responseStream) {
yield { context: chunk }
Expand Down
16 changes: 12 additions & 4 deletions src/server/routers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import { z } from 'zod';
import { prisma } from '../prisma';
import { GlobalConfig, ZConfigKey, ZConfigSchema, ZUserPerferConfigKey } from '../types';
import { configSchema } from '@/lib/prismaZodType';
import { Context } from '../context';

export const getGlobalConfig = async (ctx?: { id?: string }) => {
export const getGlobalConfig = async ({ ctx, useAdmin = false }: { ctx?: Context, useAdmin?: boolean }) => {
const userId = Number(ctx?.id ?? 1);
const configs = await prisma.config.findMany();

const isSuperAdmin = useAdmin ? true : ctx?.role === 'superadmin';

const globalConfig = configs.reduce((acc, item) => {
const config = item.config as { type: string, value: any };
if (!isSuperAdmin && !item.userId) {
return acc;
}
const isUserPreferConfig = ZUserPerferConfigKey.safeParse(item.key).success;
if ((isUserPreferConfig && item.userId === userId) || (!isUserPreferConfig)) {
acc[item.key] = config.value;
}
return acc;
}, {} as Record<string, any>);

return globalConfig as GlobalConfig;
};

Expand All @@ -26,7 +31,7 @@ export const configRouter = router({
.input(z.void())
.output(ZConfigSchema)
.query(async function ({ ctx }) {
return await getGlobalConfig(ctx)
return await getGlobalConfig({ ctx })
}),
update: authProcedure
.meta({ openapi: { method: 'POST', path: '/v1/config/update', summary: 'Update user config', protect: true, tags: ['Config'] } })
Expand All @@ -46,6 +51,9 @@ export const configRouter = router({
}
return await prisma.config.create({ data: { userId, key, config: { type: typeof value, value } } })
} else {
if (ctx.role !== 'superadmin') {
throw new Error('You are not allowed to update global config')
}
// global config
const hasKey = await prisma.config.findFirst({ where: { key } })
if (hasKey) {
Expand Down
2 changes: 1 addition & 1 deletion src/server/routers/helper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getGlobalConfig } from "../config"

export const SendWebhook = async (data: any, webhookType: string, ctx: { id: string }) => {
try {
const globalConfig = await getGlobalConfig(ctx)
const globalConfig = await getGlobalConfig({ useAdmin: true })
if (globalConfig.webhookEndpoint) {
await axios.post(globalConfig.webhookEndpoint, { data, webhookType, activityType: `blinko.note.${webhookType}` })
}
Expand Down
Loading

0 comments on commit 0aec69e

Please sign in to comment.