mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
fixes
This commit is contained in:
@@ -88,7 +88,7 @@ export function extractBearerTokenFromHeader(
|
|||||||
request: FastifyRequest,
|
request: FastifyRequest,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
||||||
return type === 'Bearer' ? token : undefined;
|
return type?.toLowerCase() === 'bearer' ? token : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasLicenseOrEE(opts: {
|
export function hasLicenseOrEE(opts: {
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import { type Kysely, sql } from 'kysely';
|
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
|
||||||
await db.schema
|
|
||||||
.createTable('scim_tokens')
|
|
||||||
.addColumn('id', 'uuid', (col) =>
|
|
||||||
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
|
|
||||||
)
|
|
||||||
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
||||||
|
|
||||||
.addColumn('token', 'varchar', (col) => col.notNull())
|
|
||||||
|
|
||||||
.addColumn('expires_at', 'timestamptz', (col) => col)
|
|
||||||
|
|
||||||
.addColumn('last_used_at', 'timestamptz', (col) => col)
|
|
||||||
.addColumn('allow_signup', 'boolean', (col) =>
|
|
||||||
col.defaultTo(false).notNull(),
|
|
||||||
)
|
|
||||||
.addColumn('is_enabled', 'boolean', (col) => col.defaultTo(false).notNull())
|
|
||||||
|
|
||||||
.addColumn('creator_id', 'uuid', (col) =>
|
|
||||||
col.references('users.id').onDelete('set null'),
|
|
||||||
)
|
|
||||||
.addColumn('workspace_id', 'uuid', (col) =>
|
|
||||||
col.references('workspaces.id').onDelete('cascade').notNull(),
|
|
||||||
)
|
|
||||||
.addColumn('created_at', 'timestamptz', (col) =>
|
|
||||||
col.notNull().defaultTo(sql`now()`),
|
|
||||||
)
|
|
||||||
.addColumn('updated_at', 'timestamptz', (col) =>
|
|
||||||
col.notNull().defaultTo(sql`now()`),
|
|
||||||
)
|
|
||||||
.addColumn('deleted_at', 'timestamptz', (col) => col)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
|
||||||
.alterTable('workspaces')
|
|
||||||
.addColumn('is_scim_enabled', 'boolean', (col) =>
|
|
||||||
col.defaultTo(false).notNull(),
|
|
||||||
)
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(db: Kysely<any>): Promise<void> {
|
|
||||||
await db.schema.dropTable('scim_tokens').execute();
|
|
||||||
|
|
||||||
await db.schema
|
|
||||||
.alterTable('workspaces')
|
|
||||||
.dropColumn('is_scim_enabled')
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema
|
||||||
|
.createTable('scim_tokens')
|
||||||
|
.addColumn('id', 'uuid', (col) =>
|
||||||
|
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
|
||||||
|
)
|
||||||
|
.addColumn('name', 'varchar', (col) => col.notNull())
|
||||||
|
.addColumn('token_hash', 'varchar', (col) => col.notNull())
|
||||||
|
.addColumn('token_last_four', 'varchar(4)', (col) => col.notNull())
|
||||||
|
.addColumn('expires_at', 'timestamptz')
|
||||||
|
.addColumn('last_used_at', 'timestamptz')
|
||||||
|
.addColumn('is_enabled', 'boolean', (col) =>
|
||||||
|
col.notNull().defaultTo(true),
|
||||||
|
)
|
||||||
|
.addColumn('creator_id', 'uuid', (col) =>
|
||||||
|
col.references('users.id').onDelete('set null'),
|
||||||
|
)
|
||||||
|
.addColumn('workspace_id', 'uuid', (col) =>
|
||||||
|
col.references('workspaces.id').onDelete('cascade').notNull(),
|
||||||
|
)
|
||||||
|
.addColumn('created_at', 'timestamptz', (col) =>
|
||||||
|
col.notNull().defaultTo(sql`now()`),
|
||||||
|
)
|
||||||
|
.addColumn('updated_at', 'timestamptz', (col) =>
|
||||||
|
col.notNull().defaultTo(sql`now()`),
|
||||||
|
)
|
||||||
|
.addColumn('deleted_at', 'timestamptz')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('idx_scim_tokens_token_hash')
|
||||||
|
.on('scim_tokens')
|
||||||
|
.column('token_hash')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('idx_scim_tokens_workspace_id')
|
||||||
|
.on('scim_tokens')
|
||||||
|
.column('workspace_id')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.alterTable('users')
|
||||||
|
.addColumn('scim_external_id', 'text')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('idx_users_workspace_scim_external_id')
|
||||||
|
.on('users')
|
||||||
|
.columns(['workspace_id', 'scim_external_id'])
|
||||||
|
.where('scim_external_id', 'is not', null)
|
||||||
|
.unique()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.alterTable('groups')
|
||||||
|
.addColumn('scim_external_id', 'text')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex('idx_groups_workspace_scim_external_id')
|
||||||
|
.on('groups')
|
||||||
|
.columns(['workspace_id', 'scim_external_id'])
|
||||||
|
.where('scim_external_id', 'is not', null)
|
||||||
|
.unique()
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema.dropTable('scim_tokens').execute();
|
||||||
|
|
||||||
|
await db.schema.dropIndex('idx_users_workspace_scim_external_id').execute();
|
||||||
|
await db.schema
|
||||||
|
.alterTable('users')
|
||||||
|
.dropColumn('scim_external_id')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema.dropIndex('idx_groups_workspace_scim_external_id').execute();
|
||||||
|
await db.schema
|
||||||
|
.alterTable('groups')
|
||||||
|
.dropColumn('scim_external_id')
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
} from '@docmost/db/types/entity.types';
|
} from '@docmost/db/types/entity.types';
|
||||||
import { ExpressionBuilder, sql } from 'kysely';
|
import { ExpressionBuilder, sql } from 'kysely';
|
||||||
import { PaginationOptions } from '../../pagination/pagination-options';
|
import { PaginationOptions } from '../../pagination/pagination-options';
|
||||||
import { DB } from '@docmost/db/types/db';
|
import { DB, Groups } from '@docmost/db/types/db';
|
||||||
import { DefaultGroup } from '../../../core/group/dto/create-group.dto';
|
import { DefaultGroup } from '../../../core/group/dto/create-group.dto';
|
||||||
import { executeWithCursorPagination } from '@docmost/db/pagination/cursor-pagination';
|
import { executeWithCursorPagination } from '@docmost/db/pagination/cursor-pagination';
|
||||||
|
|
||||||
@@ -17,16 +17,33 @@ import { executeWithCursorPagination } from '@docmost/db/pagination/cursor-pagin
|
|||||||
export class GroupRepo {
|
export class GroupRepo {
|
||||||
constructor(@InjectKysely() private readonly db: KyselyDB) {}
|
constructor(@InjectKysely() private readonly db: KyselyDB) {}
|
||||||
|
|
||||||
|
private baseFields: Array<keyof Groups> = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'isDefault',
|
||||||
|
'creatorId',
|
||||||
|
'workspaceId',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'deletedAt',
|
||||||
|
];
|
||||||
|
|
||||||
async findById(
|
async findById(
|
||||||
groupId: string,
|
groupId: string,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
opts?: { includeMemberCount?: boolean; trx?: KyselyTransaction },
|
opts?: {
|
||||||
|
includeMemberCount?: boolean;
|
||||||
|
includeScimExternalId?: boolean;
|
||||||
|
trx?: KyselyTransaction;
|
||||||
|
},
|
||||||
): Promise<Group> {
|
): Promise<Group> {
|
||||||
const db = dbOrTx(this.db, opts?.trx);
|
const db = dbOrTx(this.db, opts?.trx);
|
||||||
return db
|
return db
|
||||||
.selectFrom('groups')
|
.selectFrom('groups')
|
||||||
.selectAll('groups')
|
.select(this.baseFields)
|
||||||
.$if(opts?.includeMemberCount, (qb) => qb.select(this.withMemberCount))
|
.$if(opts?.includeMemberCount, (qb) => qb.select(this.withMemberCount))
|
||||||
|
.$if(opts?.includeScimExternalId, (qb) => qb.select('scimExternalId'))
|
||||||
.where('id', '=', groupId)
|
.where('id', '=', groupId)
|
||||||
.where('workspaceId', '=', workspaceId)
|
.where('workspaceId', '=', workspaceId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
@@ -35,13 +52,18 @@ export class GroupRepo {
|
|||||||
async findByName(
|
async findByName(
|
||||||
groupName: string,
|
groupName: string,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
opts?: { includeMemberCount?: boolean; trx?: KyselyTransaction },
|
opts?: {
|
||||||
|
includeMemberCount?: boolean;
|
||||||
|
includeScimExternalId?: boolean;
|
||||||
|
trx?: KyselyTransaction;
|
||||||
|
},
|
||||||
): Promise<Group> {
|
): Promise<Group> {
|
||||||
const db = dbOrTx(this.db, opts?.trx);
|
const db = dbOrTx(this.db, opts?.trx);
|
||||||
return db
|
return db
|
||||||
.selectFrom('groups')
|
.selectFrom('groups')
|
||||||
.selectAll('groups')
|
.select(this.baseFields)
|
||||||
.$if(opts?.includeMemberCount, (qb) => qb.select(this.withMemberCount))
|
.$if(opts?.includeMemberCount, (qb) => qb.select(this.withMemberCount))
|
||||||
|
.$if(opts?.includeScimExternalId, (qb) => qb.select('scimExternalId'))
|
||||||
.where(sql`LOWER(name)`, '=', sql`LOWER(${groupName})`)
|
.where(sql`LOWER(name)`, '=', sql`LOWER(${groupName})`)
|
||||||
.where('workspaceId', '=', workspaceId)
|
.where('workspaceId', '=', workspaceId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
@@ -71,7 +93,7 @@ export class GroupRepo {
|
|||||||
return db
|
return db
|
||||||
.insertInto('groups')
|
.insertInto('groups')
|
||||||
.values(insertableGroup)
|
.values(insertableGroup)
|
||||||
.returningAll()
|
.returning(this.baseFields)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +105,7 @@ export class GroupRepo {
|
|||||||
return (
|
return (
|
||||||
db
|
db
|
||||||
.selectFrom('groups')
|
.selectFrom('groups')
|
||||||
.selectAll()
|
.select(this.baseFields)
|
||||||
// .select((eb) => this.withMemberCount(eb))
|
// .select((eb) => this.withMemberCount(eb))
|
||||||
.where('isDefault', '=', true)
|
.where('isDefault', '=', true)
|
||||||
.where('workspaceId', '=', workspaceId)
|
.where('workspaceId', '=', workspaceId)
|
||||||
@@ -109,7 +131,7 @@ export class GroupRepo {
|
|||||||
async getGroupsPaginated(workspaceId: string, pagination: PaginationOptions) {
|
async getGroupsPaginated(workspaceId: string, pagination: PaginationOptions) {
|
||||||
let baseQuery = this.db
|
let baseQuery = this.db
|
||||||
.selectFrom('groups')
|
.selectFrom('groups')
|
||||||
.selectAll('groups')
|
.select(this.baseFields)
|
||||||
.select((eb) => this.withMemberCount(eb))
|
.select((eb) => this.withMemberCount(eb))
|
||||||
.where('workspaceId', '=', workspaceId);
|
.where('workspaceId', '=', workspaceId);
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export class UserRepo {
|
|||||||
opts?: {
|
opts?: {
|
||||||
includePassword?: boolean;
|
includePassword?: boolean;
|
||||||
includeUserMfa?: boolean;
|
includeUserMfa?: boolean;
|
||||||
|
includeScimExternalId?: boolean;
|
||||||
trx?: KyselyTransaction;
|
trx?: KyselyTransaction;
|
||||||
},
|
},
|
||||||
): Promise<User> {
|
): Promise<User> {
|
||||||
@@ -52,6 +53,7 @@ export class UserRepo {
|
|||||||
.select(this.baseFields)
|
.select(this.baseFields)
|
||||||
.$if(opts?.includePassword, (qb) => qb.select('password'))
|
.$if(opts?.includePassword, (qb) => qb.select('password'))
|
||||||
.$if(opts?.includeUserMfa, (qb) => qb.select(this.withUserMfa))
|
.$if(opts?.includeUserMfa, (qb) => qb.select(this.withUserMfa))
|
||||||
|
.$if(opts?.includeScimExternalId, (qb) => qb.select('scimExternalId'))
|
||||||
.where('id', '=', userId)
|
.where('id', '=', userId)
|
||||||
.where('workspaceId', '=', workspaceId)
|
.where('workspaceId', '=', workspaceId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
@@ -63,6 +65,7 @@ export class UserRepo {
|
|||||||
opts?: {
|
opts?: {
|
||||||
includePassword?: boolean;
|
includePassword?: boolean;
|
||||||
includeUserMfa?: boolean;
|
includeUserMfa?: boolean;
|
||||||
|
includeScimExternalId?: boolean;
|
||||||
trx?: KyselyTransaction;
|
trx?: KyselyTransaction;
|
||||||
},
|
},
|
||||||
): Promise<User> {
|
): Promise<User> {
|
||||||
@@ -72,6 +75,7 @@ export class UserRepo {
|
|||||||
.select(this.baseFields)
|
.select(this.baseFields)
|
||||||
.$if(opts?.includePassword, (qb) => qb.select('password'))
|
.$if(opts?.includePassword, (qb) => qb.select('password'))
|
||||||
.$if(opts?.includeUserMfa, (qb) => qb.select(this.withUserMfa))
|
.$if(opts?.includeUserMfa, (qb) => qb.select(this.withUserMfa))
|
||||||
|
.$if(opts?.includeScimExternalId, (qb) => qb.select('scimExternalId'))
|
||||||
.where(sql`LOWER(email)`, '=', sql`LOWER(${email})`)
|
.where(sql`LOWER(email)`, '=', sql`LOWER(${email})`)
|
||||||
.where('workspaceId', '=', workspaceId)
|
.where('workspaceId', '=', workspaceId)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
|
|||||||
+18
@@ -185,6 +185,7 @@ export interface Groups {
|
|||||||
id: Generated<string>;
|
id: Generated<string>;
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
|
scimExternalId: string | null;
|
||||||
updatedAt: Generated<Timestamp>;
|
updatedAt: Generated<Timestamp>;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
}
|
}
|
||||||
@@ -309,6 +310,7 @@ export interface Users {
|
|||||||
name: string | null;
|
name: string | null;
|
||||||
password: string | null;
|
password: string | null;
|
||||||
role: string | null;
|
role: string | null;
|
||||||
|
scimExternalId: string | null;
|
||||||
settings: Json | null;
|
settings: Json | null;
|
||||||
timezone: string | null;
|
timezone: string | null;
|
||||||
updatedAt: Generated<Timestamp>;
|
updatedAt: Generated<Timestamp>;
|
||||||
@@ -378,6 +380,21 @@ export interface Notifications {
|
|||||||
createdAt: Generated<Timestamp>;
|
createdAt: Generated<Timestamp>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScimTokens {
|
||||||
|
createdAt: Generated<Timestamp>;
|
||||||
|
deletedAt: Timestamp | null;
|
||||||
|
expiresAt: Timestamp | null;
|
||||||
|
id: Generated<string>;
|
||||||
|
isEnabled: Generated<boolean>;
|
||||||
|
lastUsedAt: Timestamp | null;
|
||||||
|
name: string;
|
||||||
|
tokenHash: string;
|
||||||
|
tokenLastFour: string;
|
||||||
|
creatorId: string | null;
|
||||||
|
updatedAt: Generated<Timestamp>;
|
||||||
|
workspaceId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Watchers {
|
export interface Watchers {
|
||||||
id: Generated<string>;
|
id: Generated<string>;
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -404,6 +421,7 @@ export interface DB {
|
|||||||
notifications: Notifications;
|
notifications: Notifications;
|
||||||
pageHistory: PageHistory;
|
pageHistory: PageHistory;
|
||||||
pages: Pages;
|
pages: Pages;
|
||||||
|
scimTokens: ScimTokens;
|
||||||
shares: Shares;
|
shares: Shares;
|
||||||
spaceMembers: SpaceMembers;
|
spaceMembers: SpaceMembers;
|
||||||
spaces: Spaces;
|
spaces: Spaces;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
FileTasks,
|
FileTasks,
|
||||||
UserMfa as _UserMFA,
|
UserMfa as _UserMFA,
|
||||||
ApiKeys,
|
ApiKeys,
|
||||||
|
ScimTokens,
|
||||||
Watchers,
|
Watchers,
|
||||||
} from './db';
|
} from './db';
|
||||||
import { PageEmbeddings } from '@docmost/db/types/embeddings.types';
|
import { PageEmbeddings } from '@docmost/db/types/embeddings.types';
|
||||||
@@ -129,6 +130,11 @@ export type ApiKey = Selectable<ApiKeys>;
|
|||||||
export type InsertableApiKey = Insertable<ApiKeys>;
|
export type InsertableApiKey = Insertable<ApiKeys>;
|
||||||
export type UpdatableApiKey = Updateable<Omit<ApiKeys, 'id'>>;
|
export type UpdatableApiKey = Updateable<Omit<ApiKeys, 'id'>>;
|
||||||
|
|
||||||
|
// Scim Tokens
|
||||||
|
export type ScimToken = Selectable<ScimTokens>;
|
||||||
|
export type InsertableScimToken = Insertable<ScimTokens>;
|
||||||
|
export type UpdatableScimToken = Updateable<Omit<ScimTokens, 'id'>>;
|
||||||
|
|
||||||
// Page Embedding
|
// Page Embedding
|
||||||
export type PageEmbedding = Selectable<PageEmbeddings>;
|
export type PageEmbedding = Selectable<PageEmbeddings>;
|
||||||
export type InsertablePageEmbedding = Insertable<PageEmbeddings>;
|
export type InsertablePageEmbedding = Insertable<PageEmbeddings>;
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: a3b48000bd...bcf4a6967b
Reference in New Issue
Block a user