mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-05 17:13:43 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
776ca2c702 | ||
|
|
903e0c4d71 | ||
|
|
f29e47e9d2 | ||
|
|
6e6b85098e | ||
|
|
cf14accd2b | ||
|
|
cf4e37c584 | ||
|
|
69cdeedc4e | ||
|
|
0495fac6f1 | ||
|
|
5cac8971eb | ||
|
|
1196101226 | ||
|
|
bcc892c8ec | ||
|
|
88a2e4aa4b | ||
|
|
0bedaaadba | ||
|
|
1d9fe3b8d9 | ||
|
|
33a014977a | ||
|
|
221c493c56 | ||
|
|
6c36fc5941 | ||
|
|
477e6f4106 | ||
|
|
b7ebe3d0d6 | ||
|
|
20ba8875c1 | ||
|
|
8544e58c01 | ||
|
|
36a08190e0 | ||
|
|
b229c96ee5 | ||
|
|
62fe6982fb |
2
.github/helm/affine/Chart.yaml
vendored
2
.github/helm/affine/Chart.yaml
vendored
@@ -3,4 +3,4 @@ name: affine
|
||||
description: AFFiNE cloud chart
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.25.5"
|
||||
appVersion: "0.25.7"
|
||||
|
||||
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: doc
|
||||
description: AFFiNE doc server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.25.5"
|
||||
appVersion: "0.25.7"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
@@ -3,7 +3,7 @@ name: graphql
|
||||
description: AFFiNE GraphQL server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.25.5"
|
||||
appVersion: "0.25.7"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
@@ -3,7 +3,7 @@ name: renderer
|
||||
description: AFFiNE renderer server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.25.5"
|
||||
appVersion: "0.25.7"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
2
.github/helm/affine/charts/sync/Chart.yaml
vendored
2
.github/helm/affine/charts/sync/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: sync
|
||||
description: AFFiNE Sync Server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.25.5"
|
||||
appVersion: "0.25.7"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -125,18 +125,23 @@ dependencies = [
|
||||
"affine_media_capture",
|
||||
"affine_nbstore",
|
||||
"affine_sqlite_v1",
|
||||
"chrono",
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"once_cell",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "affine_nbstore"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"affine_common",
|
||||
"affine_schema",
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -144,10 +149,14 @@ dependencies = [
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"uniffi",
|
||||
"uuid",
|
||||
"y-octo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5",
|
||||
"version": "0.25.7",
|
||||
"devDependencies": {
|
||||
"@vanilla-extract/vite-plugin": "^5.0.0",
|
||||
"msw": "^2.8.4",
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ export class DatabaseTitle extends SignalWatcher(
|
||||
private readonly isFocus$ = signal(false);
|
||||
|
||||
private onPressEnterKey() {
|
||||
this.dataViewLogic.addRow?.('start');
|
||||
this.input.blur();
|
||||
}
|
||||
|
||||
get readonly$() {
|
||||
|
||||
@@ -39,5 +39,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
// Caches total bounds, includes all blocks and elements.
|
||||
private _cachedBounds: Bound | null = null;
|
||||
|
||||
private _hasRenderedSyncedView = false;
|
||||
private _hasInitedFitEffect = false;
|
||||
|
||||
private readonly _initEdgelessFitEffect = () => {
|
||||
const fitToContent = () => {
|
||||
if (this.isPageMode) return;
|
||||
@@ -558,8 +561,6 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
this._selectBlock();
|
||||
}
|
||||
});
|
||||
|
||||
this._initEdgelessFitEffect();
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
@@ -587,12 +588,21 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
);
|
||||
}
|
||||
|
||||
!this._hasRenderedSyncedView && (this._hasRenderedSyncedView = true);
|
||||
|
||||
return this._renderSyncedView();
|
||||
}
|
||||
|
||||
override updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
this.syncedDocCard?.requestUpdate();
|
||||
|
||||
if (!this._hasInitedFitEffect && this._hasRenderedSyncedView) {
|
||||
/* Register the resizeObserver AFTER syncdView viewport's own resizeObserver
|
||||
* so that viewport.onResize() use up-to-date boundingClientRect values */
|
||||
this._hasInitedFitEffect = true;
|
||||
this._initEdgelessFitEffect();
|
||||
}
|
||||
}
|
||||
|
||||
@state()
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -67,5 +67,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ export class CanvasRenderer {
|
||||
* It is not recommended to set width and height to 100%.
|
||||
*/
|
||||
private _canvasSizeUpdater(dpr = window.devicePixelRatio) {
|
||||
const { width, height } = this.viewport;
|
||||
const { width, height, viewScale } = this.viewport;
|
||||
const actualWidth = Math.ceil(width * dpr);
|
||||
const actualHeight = Math.ceil(height * dpr);
|
||||
|
||||
@@ -124,6 +124,8 @@ export class CanvasRenderer {
|
||||
update(canvas: HTMLCanvasElement) {
|
||||
canvas.style.width = `${width}px`;
|
||||
canvas.style.height = `${height}px`;
|
||||
canvas.style.transform = `scale(${1 / viewScale})`;
|
||||
canvas.style.transformOrigin = `top left`;
|
||||
canvas.width = actualWidth;
|
||||
canvas.height = actualHeight;
|
||||
},
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -82,5 +82,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export type MenuInputData = {
|
||||
class?: string;
|
||||
onComplete?: (value: string) => void;
|
||||
onChange?: (value: string) => void;
|
||||
onBlur?: (value: string) => void;
|
||||
disableAutoFocus?: boolean;
|
||||
};
|
||||
|
||||
@@ -49,6 +50,10 @@ export class MenuInput extends MenuFocusable {
|
||||
this.data.onChange?.(this.inputRef.value);
|
||||
};
|
||||
|
||||
private readonly onBlur = () => {
|
||||
this.data.onBlur?.(this.inputRef.value);
|
||||
};
|
||||
|
||||
private readonly onInput = (e: InputEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.isComposing) return;
|
||||
@@ -109,6 +114,7 @@ export class MenuInput extends MenuFocusable {
|
||||
@focus="${() => {
|
||||
this.menu.setFocusOnly(this);
|
||||
}}"
|
||||
@blur="${this.onBlur}"
|
||||
@input="${this.onInput}"
|
||||
placeholder="${this.data.placeholder ?? ''}"
|
||||
@keypress="${this.stopPropagation}"
|
||||
@@ -215,6 +221,7 @@ export const menuInputItems = {
|
||||
prefix?: TemplateResult;
|
||||
onComplete?: (value: string) => void;
|
||||
onChange?: (value: string) => void;
|
||||
onBlur?: (value: string) => void;
|
||||
class?: string;
|
||||
style?: Readonly<StyleInfo>;
|
||||
}) =>
|
||||
@@ -228,6 +235,7 @@ export const menuInputItems = {
|
||||
class: config.class,
|
||||
onComplete: config.onComplete,
|
||||
onChange: config.onChange,
|
||||
onBlur: config.onBlur,
|
||||
};
|
||||
const style = styleMap({
|
||||
display: 'flex',
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -1,25 +1,10 @@
|
||||
import { menu } from '@blocksuite/affine-components/context-menu';
|
||||
import { IS_MOBILE } from '@blocksuite/global/env';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import { renderUniLit } from '../utils/uni-component/index.js';
|
||||
import type { Property } from '../view-manager/property.js';
|
||||
|
||||
export const inputConfig = (property: Property) => {
|
||||
if (IS_MOBILE) {
|
||||
return menu.input({
|
||||
prefix: html`
|
||||
<div class="affine-database-column-type-menu-icon">
|
||||
${renderUniLit(property.icon)}
|
||||
</div>
|
||||
`,
|
||||
initialValue: property.name$.value,
|
||||
placeholder: 'Property name',
|
||||
onChange: text => {
|
||||
property.nameSet(text);
|
||||
},
|
||||
});
|
||||
}
|
||||
return menu.input({
|
||||
prefix: html`
|
||||
<div class="affine-database-column-type-menu-icon">
|
||||
@@ -28,7 +13,7 @@ export const inputConfig = (property: Property) => {
|
||||
`,
|
||||
initialValue: property.name$.value,
|
||||
placeholder: 'Property name',
|
||||
onComplete: text => {
|
||||
onBlur: text => {
|
||||
property.nameSet(text);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -26,5 +26,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -51,5 +51,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -60,12 +60,20 @@ export class PanTool extends BaseTool<PanToolOption> {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTool = this.controller.currentToolOption$.peek();
|
||||
const { toolType, options: originalToolOptions } = currentTool;
|
||||
|
||||
if (toolType?.toolName === PanTool.toolName) {
|
||||
return;
|
||||
}
|
||||
|
||||
evt.raw.preventDefault();
|
||||
|
||||
const currentTool = this.controller.currentToolOption$.peek();
|
||||
const selectionToRestore = this.gfx.selection.surfaceSelections.slice();
|
||||
|
||||
const restoreToPrevious = () => {
|
||||
const { toolType, options: originalToolOptions } = currentTool;
|
||||
const selectionToRestore = this.gfx.selection.surfaceSelections;
|
||||
this.gfx.selection.set(selectionToRestore);
|
||||
|
||||
if (!toolType) return;
|
||||
// restore to DefaultTool if previous tool is CopilotTool
|
||||
if (toolType.toolName === 'copilot') {
|
||||
@@ -88,21 +96,18 @@ export class PanTool extends BaseTool<PanToolOption> {
|
||||
} as RestorablePresentToolOptions;
|
||||
}
|
||||
this.controller.setTool(toolType, finalOptions);
|
||||
this.gfx.selection.set(selectionToRestore);
|
||||
};
|
||||
|
||||
// If in presentation mode, disable black background after middle mouse drag
|
||||
if (currentTool.toolType?.toolName === 'frameNavigator') {
|
||||
if (toolType?.toolName === 'frameNavigator') {
|
||||
const slots = this.std.get(EdgelessLegacySlotIdentifier);
|
||||
slots.navigatorSettingUpdated.next({
|
||||
blackBackground: false,
|
||||
});
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this.controller.setTool(PanTool, {
|
||||
panning: true,
|
||||
});
|
||||
this.controller.setTool(PanTool, {
|
||||
panning: true,
|
||||
});
|
||||
|
||||
const dispose = on(document, 'pointerup', evt => {
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -47,5 +47,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -50,5 +50,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -56,5 +56,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -75,5 +75,5 @@
|
||||
"devDependencies": {
|
||||
"vitest": "3.1.3"
|
||||
},
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -38,5 +38,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -38,5 +38,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"dependencies": {
|
||||
"@blocksuite/affine": "workspace:*"
|
||||
},
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -64,5 +64,5 @@
|
||||
"devDependencies": {
|
||||
"vitest": "3.1.3"
|
||||
},
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -47,5 +47,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!dist/__tests__",
|
||||
"shim.d.ts"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"vite-plugin-wasm": "^3.4.1",
|
||||
"vitest": "3.1.3"
|
||||
},
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"vite-plugin-wasm": "^3.3.0",
|
||||
"vite-plugin-web-components-hmr": "^0.1.3"
|
||||
},
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -19,5 +19,5 @@
|
||||
],
|
||||
"ext": "ts,md,json"
|
||||
},
|
||||
"version": "0.25.5"
|
||||
"version": "0.25.7"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@affine/monorepo",
|
||||
"version": "0.25.5",
|
||||
"version": "0.25.7",
|
||||
"private": true,
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -7,7 +7,7 @@ version = "1.0.0"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
affine_common = { workspace = true, features = ["doc-loader"] }
|
||||
affine_common = { workspace = true, features = ["doc-loader", "hashcash"] }
|
||||
chrono = { workspace = true }
|
||||
file-format = { workspace = true }
|
||||
infer = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@affine/server-native",
|
||||
"version": "0.25.5",
|
||||
"version": "0.25.7",
|
||||
"engines": {
|
||||
"node": ">= 10.16.0 < 11 || >= 11.8.0"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@affine/server",
|
||||
"private": true,
|
||||
"version": "0.25.5",
|
||||
"version": "0.25.7",
|
||||
"description": "Affine Node.js server",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
@@ -95,6 +95,7 @@
|
||||
"http-errors": "^2.0.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"is-mobile": "^5.0.0",
|
||||
"jose": "^6.1.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"keyv": "^5.2.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -331,7 +331,6 @@ function mockOAuthProvider(
|
||||
clientNonce,
|
||||
});
|
||||
|
||||
// @ts-expect-error mock
|
||||
Sinon.stub(provider, 'getToken').resolves({ accessToken: '1' });
|
||||
Sinon.stub(provider, 'getUser').resolves({
|
||||
id: '1',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Transactional } from '@nestjs-cls/transactional';
|
||||
import { TransactionalAdapterPrisma } from '@nestjs-cls/transactional-adapter-prisma';
|
||||
import {
|
||||
applyUpdate,
|
||||
diffUpdate,
|
||||
@@ -94,7 +95,7 @@ export abstract class DocStorageAdapter extends Connection {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@Transactional()
|
||||
@Transactional<TransactionalAdapterPrisma>({ timeout: 60000 })
|
||||
private async squashUpdatesToSnapshot(
|
||||
spaceId: string,
|
||||
docId: string,
|
||||
|
||||
@@ -580,7 +580,6 @@ export class CopilotSessionModel extends BaseModel {
|
||||
.reduce((prev, cost) => prev + cost, 0);
|
||||
}
|
||||
|
||||
@Transactional()
|
||||
async cleanupEmptySessions(earlyThen: Date) {
|
||||
// delete never used sessions
|
||||
const { count: removed } = await this.db.aiSession.deleteMany({
|
||||
|
||||
@@ -185,7 +185,6 @@ export class CopilotWorkspaceConfigModel extends BaseModel {
|
||||
return { workspaceId, AND: condition };
|
||||
}
|
||||
|
||||
@Transactional()
|
||||
async listEmbeddableDocIds(workspaceId: string) {
|
||||
const condition = this.getEmbeddableCondition(workspaceId);
|
||||
const rows = await this.db.snapshot.findMany({
|
||||
|
||||
@@ -416,12 +416,12 @@ export class CopilotEmbeddingJob {
|
||||
workspaceId,
|
||||
docId
|
||||
);
|
||||
this.logger.log(
|
||||
this.logger.debug(
|
||||
`Check if doc ${docId} in workspace ${workspaceId} needs embedding: ${needEmbedding}`
|
||||
);
|
||||
if (needEmbedding) {
|
||||
if (signal.aborted) {
|
||||
this.logger.log(
|
||||
this.logger.debug(
|
||||
`Doc ${docId} in workspace ${workspaceId} is aborted, skipping embedding.`
|
||||
);
|
||||
return;
|
||||
@@ -442,7 +442,7 @@ export class CopilotEmbeddingJob {
|
||||
existsContent &&
|
||||
this.normalize(existsContent) === this.normalize(fragment.summary)
|
||||
) {
|
||||
this.logger.log(
|
||||
this.logger.debug(
|
||||
`Doc ${docId} in workspace ${workspaceId} has no content change, skipping embedding.`
|
||||
);
|
||||
return;
|
||||
@@ -464,12 +464,12 @@ export class CopilotEmbeddingJob {
|
||||
chunks
|
||||
);
|
||||
}
|
||||
this.logger.log(
|
||||
this.logger.debug(
|
||||
`Doc ${docId} in workspace ${workspaceId} has summary, embedding done.`
|
||||
);
|
||||
} else {
|
||||
// for empty doc, insert empty embedding
|
||||
this.logger.warn(
|
||||
this.logger.debug(
|
||||
`Doc ${docId} in workspace ${workspaceId} has no summary, fulfilling empty embedding.`
|
||||
);
|
||||
await this.models.copilotContext.fulfillEmptyEmbedding(
|
||||
@@ -478,7 +478,7 @@ export class CopilotEmbeddingJob {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(
|
||||
this.logger.debug(
|
||||
`Doc ${docId} in workspace ${workspaceId} has no fragment, fulfilling empty embedding.`
|
||||
);
|
||||
await this.models.copilotContext.fulfillEmptyEmbedding(
|
||||
@@ -498,7 +498,7 @@ export class CopilotEmbeddingJob {
|
||||
error instanceof CopilotContextFileNotSupported &&
|
||||
error.message.includes('no content found')
|
||||
) {
|
||||
this.logger.warn(
|
||||
this.logger.debug(
|
||||
`Doc ${docId} in workspace ${workspaceId} has no content, fulfilling empty embedding.`
|
||||
);
|
||||
// if the doc is empty, we still need to fulfill the embedding
|
||||
|
||||
@@ -689,6 +689,7 @@ You are a highly accomplished professional translator, demonstrating profound pr
|
||||
params: {
|
||||
language: [
|
||||
'English',
|
||||
'Brazilian Portuguese',
|
||||
'Spanish',
|
||||
'German',
|
||||
'French',
|
||||
@@ -708,6 +709,7 @@ You are a highly accomplished professional translator, demonstrating profound pr
|
||||
params: {
|
||||
language: [
|
||||
'English',
|
||||
'Brazilian Portuguese',
|
||||
'Spanish',
|
||||
'German',
|
||||
'French',
|
||||
|
||||
@@ -4,7 +4,7 @@ import { defineModuleConfig, JSONSchema } from '../../base';
|
||||
|
||||
export interface OAuthProviderConfig {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
clientSecret?: string;
|
||||
args?: Record<string, string>;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export type OIDCArgs = {
|
||||
claim_id?: string;
|
||||
claim_email?: string;
|
||||
claim_name?: string;
|
||||
claim_email_verified?: string;
|
||||
};
|
||||
|
||||
export interface OAuthOIDCProviderConfig extends OAuthProviderConfig {
|
||||
@@ -88,6 +89,7 @@ defineModuleConfig('oauth', {
|
||||
claim_id: z.string().optional(),
|
||||
claim_email: z.string().optional(),
|
||||
claim_name: z.string().optional(),
|
||||
claim_email_verified: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -65,18 +65,37 @@ export class OAuthController {
|
||||
throw new UnknownOauthProvider({ name: unknownProviderName });
|
||||
}
|
||||
|
||||
const pkce = provider.requiresPkce ? this.oauth.createPkcePair() : null;
|
||||
|
||||
const state = await this.oauth.saveOAuthState({
|
||||
provider: providerName,
|
||||
redirectUri,
|
||||
client,
|
||||
clientNonce,
|
||||
...(pkce
|
||||
? {
|
||||
pkce: {
|
||||
codeVerifier: pkce.codeVerifier,
|
||||
codeChallengeMethod: pkce.codeChallengeMethod,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
const stateStr = JSON.stringify({
|
||||
const statePayload: Record<string, unknown> = {
|
||||
state,
|
||||
client,
|
||||
provider: unknownProviderName,
|
||||
});
|
||||
};
|
||||
|
||||
if (pkce) {
|
||||
statePayload.pkce = {
|
||||
codeChallenge: pkce.codeChallenge,
|
||||
codeChallengeMethod: pkce.codeChallengeMethod,
|
||||
};
|
||||
}
|
||||
|
||||
const stateStr = JSON.stringify(statePayload);
|
||||
|
||||
return {
|
||||
url: provider.getAuthUrl(stateStr, clientNonce),
|
||||
@@ -125,6 +144,9 @@ export class OAuthController {
|
||||
if (!state) {
|
||||
throw new OauthStateExpired();
|
||||
}
|
||||
if (!state.token) {
|
||||
state.token = stateStr;
|
||||
}
|
||||
|
||||
if (
|
||||
state.provider === OAuthProviderName.Apple &&
|
||||
@@ -173,7 +195,7 @@ export class OAuthController {
|
||||
|
||||
let tokens: Tokens;
|
||||
try {
|
||||
tokens = await provider.getToken(code);
|
||||
tokens = await provider.getToken(code, state);
|
||||
} catch (err) {
|
||||
let rayBodyString = '';
|
||||
if (req.rawBody) {
|
||||
@@ -238,6 +260,7 @@ export class OAuthController {
|
||||
}
|
||||
|
||||
const user = await this.models.user.fulfill(externalAccount.email, {
|
||||
name: externalAccount.name,
|
||||
avatarUrl: externalAccount.avatarUrl,
|
||||
});
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@ import { JsonWebKey } from 'node:crypto';
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import jwt, { type JwtPayload } from 'jsonwebtoken';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
InternalServerError,
|
||||
InvalidOauthCallbackCode,
|
||||
InvalidAuthState,
|
||||
URLHelper,
|
||||
} from '../../../base';
|
||||
import { OAuthProviderName } from '../config';
|
||||
import type { OAuthState } from '../types';
|
||||
import { OAuthProvider, Tokens } from './def';
|
||||
|
||||
interface AuthTokenResponse {
|
||||
@@ -19,14 +21,75 @@ interface AuthTokenResponse {
|
||||
expires_in: number;
|
||||
}
|
||||
|
||||
const AppleProviderArgsSchema = z.object({
|
||||
privateKey: z.string().nonempty(),
|
||||
keyId: z.string().nonempty(),
|
||||
teamId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
export class AppleOAuthProvider extends OAuthProvider {
|
||||
provider = OAuthProviderName.Apple;
|
||||
private args: z.infer<typeof AppleProviderArgsSchema> | null = null;
|
||||
private _jwtCache: { token: string; expiresAt: number } | null = null;
|
||||
|
||||
constructor(private readonly url: URLHelper) {
|
||||
super();
|
||||
}
|
||||
|
||||
override get configured() {
|
||||
if (this.config && !this.args) {
|
||||
const result = AppleProviderArgsSchema.safeParse(this.config?.args);
|
||||
if (result.success) {
|
||||
this.args = result.data;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
!!this.config &&
|
||||
!!this.config.clientId &&
|
||||
(!!this.config.clientSecret || !!this.args)
|
||||
);
|
||||
}
|
||||
|
||||
private get clientSecret() {
|
||||
if (this.config.clientSecret) {
|
||||
return this.config.clientSecret;
|
||||
}
|
||||
|
||||
if (!this.args) {
|
||||
throw new Error('Missing Apple OAuth configuration');
|
||||
}
|
||||
|
||||
if (this._jwtCache && this._jwtCache.expiresAt > Date.now()) {
|
||||
return this._jwtCache.token;
|
||||
}
|
||||
|
||||
const { privateKey, keyId, teamId } = this.args;
|
||||
const expiresIn = 300; // 5 minutes
|
||||
|
||||
try {
|
||||
const token = jwt.sign({}, privateKey, {
|
||||
algorithm: 'ES256',
|
||||
keyid: keyId,
|
||||
expiresIn,
|
||||
issuer: teamId,
|
||||
audience: 'https://appleid.apple.com',
|
||||
subject: this.config.clientId,
|
||||
});
|
||||
|
||||
this._jwtCache = {
|
||||
token,
|
||||
expiresAt: Date.now() + (expiresIn - 30) * 1000,
|
||||
};
|
||||
|
||||
return token;
|
||||
} catch (e) {
|
||||
this.logger.error('Failed to generate Apple client secret JWT', e);
|
||||
throw new Error('Failed to generate client secret');
|
||||
}
|
||||
}
|
||||
|
||||
getAuthUrl(state: string, clientNonce?: string): string {
|
||||
return `https://appleid.apple.com/auth/authorize?${this.url.stringify({
|
||||
client_id: this.config.clientId,
|
||||
@@ -40,54 +103,39 @@ export class AppleOAuthProvider extends OAuthProvider {
|
||||
})}`;
|
||||
}
|
||||
|
||||
async getToken(code: string) {
|
||||
const response = await fetch('https://appleid.apple.com/auth/token', {
|
||||
method: 'POST',
|
||||
body: this.url.stringify({
|
||||
async getToken(code: string, _state: OAuthState) {
|
||||
const appleToken = await this.postFormJson<AuthTokenResponse>(
|
||||
'https://appleid.apple.com/auth/token',
|
||||
this.url.stringify({
|
||||
code,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret,
|
||||
client_secret: this.clientSecret,
|
||||
redirect_uri: this.url.link('/api/oauth/callback'),
|
||||
grant_type: 'authorization_code',
|
||||
}),
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const appleToken = (await response.json()) as AuthTokenResponse;
|
||||
|
||||
return {
|
||||
accessToken: appleToken.access_token,
|
||||
refreshToken: appleToken.refresh_token,
|
||||
expiresAt: new Date(Date.now() + appleToken.expires_in * 1000),
|
||||
idToken: appleToken.id_token,
|
||||
};
|
||||
} else {
|
||||
const body = await response.text();
|
||||
if (response.status < 500) {
|
||||
throw new InvalidOauthCallbackCode({ status: response.status, body });
|
||||
}
|
||||
throw new Error(
|
||||
`Server responded with non-success status ${response.status}, body: ${body}`
|
||||
);
|
||||
}
|
||||
return {
|
||||
accessToken: appleToken.access_token,
|
||||
refreshToken: appleToken.refresh_token,
|
||||
expiresAt: new Date(Date.now() + appleToken.expires_in * 1000),
|
||||
idToken: appleToken.id_token,
|
||||
};
|
||||
}
|
||||
|
||||
async getUser(
|
||||
tokens: Tokens & { idToken: string },
|
||||
state: { clientNonce: string }
|
||||
) {
|
||||
const keysReq = await fetch('https://appleid.apple.com/auth/keys', {
|
||||
method: 'GET',
|
||||
});
|
||||
const { keys } = (await keysReq.json()) as { keys: JsonWebKey[] };
|
||||
async getUser(tokens: Tokens, state: OAuthState) {
|
||||
if (!tokens.idToken) {
|
||||
throw new InvalidAuthState();
|
||||
}
|
||||
const { keys } = await this.fetchJson<{ keys: JsonWebKey[] }>(
|
||||
'https://appleid.apple.com/auth/keys',
|
||||
{ method: 'GET' },
|
||||
{ treatServerErrorAsInvalid: true }
|
||||
);
|
||||
|
||||
const payload = await new Promise<JwtPayload>((resolve, reject) => {
|
||||
jwt.verify(
|
||||
tokens.idToken,
|
||||
tokens.idToken!,
|
||||
(header, callback) => {
|
||||
const key = keys.find(key => key.kid === header.kid);
|
||||
if (!key) {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { Config, OnEvent } from '../../../base';
|
||||
import {
|
||||
Config,
|
||||
InvalidOauthCallbackCode,
|
||||
InvalidOauthResponse,
|
||||
OnEvent,
|
||||
} from '../../../base';
|
||||
import { OAuthProviderName } from '../config';
|
||||
import { OAuthProviderFactory } from '../factory';
|
||||
import type { OAuthState } from '../types';
|
||||
|
||||
export interface OAuthAccount {
|
||||
id: string;
|
||||
@@ -16,6 +22,8 @@ export interface Tokens {
|
||||
scope?: string;
|
||||
refreshToken?: string;
|
||||
expiresAt?: Date;
|
||||
idToken?: string;
|
||||
tokenType?: string;
|
||||
}
|
||||
|
||||
export interface AuthOptions {
|
||||
@@ -29,8 +37,8 @@ export interface AuthOptions {
|
||||
export abstract class OAuthProvider {
|
||||
abstract provider: OAuthProviderName;
|
||||
abstract getAuthUrl(state: string, clientNonce?: string): string;
|
||||
abstract getToken(code: string): Promise<Tokens>;
|
||||
abstract getUser(tokens: Tokens, state: any): Promise<OAuthAccount>;
|
||||
abstract getToken(code: string, state: OAuthState): Promise<Tokens>;
|
||||
abstract getUser(tokens: Tokens, state: OAuthState): Promise<OAuthAccount>;
|
||||
|
||||
protected readonly logger = new Logger(this.constructor.name);
|
||||
@Inject() private readonly factory!: OAuthProviderFactory;
|
||||
@@ -65,4 +73,63 @@ export abstract class OAuthProvider {
|
||||
this.factory.unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
get requiresPkce() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async fetchJson<T>(
|
||||
url: string,
|
||||
init?: RequestInit,
|
||||
options?: { treatServerErrorAsInvalid?: boolean }
|
||||
) {
|
||||
const response = await fetch(url, {
|
||||
headers: { Accept: 'application/json', ...init?.headers },
|
||||
...init,
|
||||
});
|
||||
|
||||
const body = await response.text();
|
||||
if (!response.ok) {
|
||||
if (response.status < 500 || options?.treatServerErrorAsInvalid) {
|
||||
throw new InvalidOauthCallbackCode({ status: response.status, body });
|
||||
}
|
||||
throw new Error(
|
||||
`Server responded with non-success status ${response.status}, body: ${body}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!body) {
|
||||
return {} as T;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(body) as T;
|
||||
} catch {
|
||||
throw new InvalidOauthResponse({
|
||||
reason: `Unable to parse JSON response from ${url}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected postFormJson<T>(
|
||||
url: string,
|
||||
body: string,
|
||||
options?: {
|
||||
headers?: Record<string, string>;
|
||||
treatServerErrorAsInvalid?: boolean;
|
||||
}
|
||||
) {
|
||||
return this.fetchJson<T>(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
...options?.headers,
|
||||
},
|
||||
},
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user