Compare commits

..

5 Commits

Author SHA1 Message Date
eyhn
b14f1cdb7c chore: update i18n metadata 2025-01-04 11:28:57 +08:00
github-actions[bot]
22a8694972 chore(i18n): sync translations (#9513)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-04 11:07:12 +08:00
doodlewind
bbe88c57b7 fix(editor): blur in edgeless content zooming (#9496)
Fix [BS-2294](https://linear.app/affine-design/issue/BS-2294/edgeless-%E7%BC%A9%E6%94%BE%E5%AD%97%E5%8F%B7%E6%A8%A1%E7%B3%8A)
2025-01-04 11:03:09 +08:00
github-actions[bot]
a3b502fad7 chore(i18n): sync translations (#9499)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-04 11:01:55 +08:00
forehalo
28f0dad3f9 fix(server): fail to load custom config (#9485)
closed #9237 #9417
2025-01-04 11:01:27 +08:00
8252 changed files with 279327 additions and 519400 deletions

View File

@@ -5,10 +5,4 @@ rustflags = ["-C", "target-feature=+crt-static"]
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-args=-Wl,--warn-unresolved-symbols"]
[target.'cfg(target_os = "macos")']
rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains", "-C", "link-args=-all_load", "-C", "link-args=-weak_framework ScreenCaptureKit"]
# https://sourceware.org/bugzilla/show_bug.cgi?id=21032
# https://sourceware.org/bugzilla/show_bug.cgi?id=21031
# https://github.com/rust-lang/rust/issues/134820
# pthread_key_create() destructors and segfault after a DSO unloading
[target.'cfg(all(target_env = "gnu", not(target_os = "windows")))']
rustflags = ["-C", "link-args=-Wl,-z,nodelete"]
rustflags = ["-C", "link-args=-all_load"]

9
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM mcr.microsoft.com/devcontainers/base:bookworm
# Install Homebrew For Linux
RUN /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && \
echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> /home/vscode/.zshrc && \
echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> /home/vscode/.bashrc && \
# Install Graphite
brew install withgraphite/tap/graphite && gt --version

View File

@@ -1,12 +1,15 @@
#!/bin/bash
# This is a script used by the devcontainer to build the project
#Enable yarn
corepack enable
corepack prepare yarn@stable --activate
# install dependencies
yarn install
# Build Server Dependencies
yarn affine @affine/server-native build
yarn affine @affine/reader build
# Create database
yarn affine @affine/server prisma migrate reset -f
yarn affine @affine/server prisma db push

View File

@@ -1,16 +1,12 @@
// For format details, see https://aka.ms/devcontainer.json.
{
"name": "AFFiNE Dev Container",
"name": "Debian",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"containerEnv": {
"COREPACK_ENABLE_DOWNLOAD_PROMPT": "0"
},
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "lts",
"installYarnUsingApt": false
"version": "18"
},
"ghcr.io/devcontainers/features/rust:1": {}
},
@@ -20,7 +16,7 @@
"extensions": [
"ms-playwright.playwright",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
"streetsidesoftware.code-spell-checker"
]
}
},

View File

@@ -2,7 +2,9 @@ version: '3.8'
services:
app:
image: mcr.microsoft.com/devcontainers/base:bookworm
build:
context: .
dockerfile: Dockerfile
volumes:
- ../..:/workspaces:cached
command: sleep infinity
@@ -10,10 +12,9 @@ services:
environment:
DATABASE_URL: postgresql://affine:affine@db:5432/affine
REDIS_SERVER_HOST: redis
AFFINE_INDEXER_SEARCH_ENDPOINT: http://indexer:9308
db:
image: pgvector/pgvector:pg16
image: postgres:latest
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
@@ -23,20 +24,8 @@ services:
POSTGRES_DB: affine
redis:
image: redis
indexer:
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.3.2}
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
memlock:
soft: -1
hard: -1
volumes:
- manticoresearch_data:/var/lib/manticore
ports:
- 6379:6379
volumes:
postgres-data:
manticoresearch_data:

View File

@@ -1,9 +1,9 @@
set -e
npm install -g @withgraphite/graphite-cli@stable
if [ -v GRAPHITE_TOKEN ];then
gt auth --token $GRAPHITE_TOKEN
fi
git fetch origin canary:canary --depth=1
git branch canary -t origin/canary
gt init --trunk canary

View File

@@ -1,15 +1,4 @@
# postgres major version
DB_VERSION=16
# database credentials
DATABASE_LOCATION=./postgres
DB_PASSWORD=affine
DB_USERNAME=affine
DB_DATABASE_NAME=affine
# elasticsearch env
# ELASTIC_VERSION=9.0.1
# enable for arm64, e.g.: macOS M1+
# ELASTIC_VERSION_ARM64=-arm64
# ELASTIC_PLATFORM=linux/arm64
# manticoresearch
MANTICORE_VERSION=9.3.2
DB_DATABASE_NAME=affine

View File

@@ -1,6 +1,3 @@
postgres
.env
compose.yml
certs/*
!certs/.gitkeep
nginx/conf.d/*
compose.yml

View File

@@ -1,27 +0,0 @@
# Dev containers
## Develop with domain
> MacOs only, OrbStack only
### 1. Generate and install Root CA
```bash
# the root ca file will be located at `./.docker/dev/certs/ca`
yarn affine cert --install
```
### 2. Generate domain certs
```bash
# certificates will be located at `./.docker/dev/certs/${domain}`
yarn affine cert --domain dev.affine.fail
```
### 3. Enable dns and nginx service in compose.yml
### 4. Add custom dns server
```bash
echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/dev.affine.fail
```

View File

@@ -3,7 +3,7 @@ services:
postgres:
env_file:
- .env
image: pgvector/pgvector:pg${DB_VERSION:-16}
image: postgres:16
ports:
- 5432:5432
environment:
@@ -24,78 +24,8 @@ services:
- 1025:1025
- 8025:8025
# https://manual.manticoresearch.com/Starting_the_server/Docker
manticoresearch:
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.3.2}
ports:
- 9308:9308
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
memlock:
soft: -1
hard: -1
volumes:
- manticoresearch_data:/var/lib/manticore
# elasticsearch:
# image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_VERSION:-9.0.1}${ELASTIC_VERSION_ARM64}
# platform: ${ELASTIC_PLATFORM}
# labels:
# co.elastic.logs/module: elasticsearch
# volumes:
# - elasticsearch_data:/usr/share/elasticsearch/data
# ports:
# - ${ES_PORT:-9200}:9200
# environment:
# - node.name=es01
# - cluster.name=affine-dev
# - discovery.type=single-node
# - bootstrap.memory_lock=true
# - xpack.security.enabled=false
# - xpack.security.http.ssl.enabled=false
# - xpack.security.transport.ssl.enabled=false
# - xpack.license.self_generated.type=basic
# mem_limit: ${ES_MEM_LIMIT:-1073741824}
# ulimits:
# memlock:
# soft: -1
# hard: -1
# healthcheck:
# test:
# [
# "CMD-SHELL",
# "curl -s http://localhost:9200 | grep -q 'affine-dev'",
# ]
# interval: 10s
# timeout: 10s
# retries: 120
# dns:
# image: strm/dnsmasq
# volumes:
# - ./dnsmasq.conf:/etc/dnsmasq.d/local.conf
# ports:
# - "53:53/udp"
# cap_add:
# - NET_ADMIN
# depends_on:
# - nginx
# nginx:
# image: nginx:alpine
# volumes:
# - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
# - ./nginx/conf.d:/etc/nginx/conf.d:ro
# - ./certs:/etc/nginx/certs:ro
# network_mode: host
networks:
dev:
volumes:
postgres_data:
manticoresearch_data:
elasticsearch_data:

View File

@@ -1,2 +0,0 @@
log-queries
address=/dev.affine.fail/127.0.0.1

View File

@@ -1,28 +0,0 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 512M;
server_names_hash_bucket_size 128;
ssi on;
gzip on;
include "/etc/nginx/conf.d/*";
}

View File

@@ -1,27 +0,0 @@
server {
listen 80;
server_name DEV_DOMAIN;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
ssl_certificate /etc/nginx/certs/$host/crt;
ssl_certificate_key /etc/nginx/certs/$host/key;
server_name DEV_DOMAIN;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
resolver 127.0.0.1;
}
}

View File

@@ -1,25 +0,0 @@
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = MN
localityName = Locality Name (eg, city)
localityName_default = Minneapolis
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Domain Control Validated
commonName = Internet Widgits Ltd
commonName_max = 64
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = DEV_DOMAIN
DNS.2 = *.DEV_DOMAIN

View File

@@ -20,4 +20,4 @@ CONFIG_LOCATION=~/.affine/self-host/config
# database credentials
DB_USERNAME=affine
DB_PASSWORD=
DB_DATABASE=affine
DB_DATABASE=affine

View File

@@ -1 +0,0 @@
.env

View File

@@ -21,7 +21,6 @@ services:
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
- AFFINE_INDEXER_ENABLED=false
restart: unless-stopped
affine_migration:
@@ -37,7 +36,6 @@ services:
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
- AFFINE_INDEXER_ENABLED=false
depends_on:
postgres:
condition: service_healthy
@@ -46,7 +44,7 @@ services:
redis:
image: redis
container_name: affine_redis
container_name: redis
healthcheck:
test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']
interval: 10s
@@ -55,8 +53,8 @@ services:
restart: unless-stopped
postgres:
image: pgvector/pgvector:pg16
container_name: affine_postgres
image: postgres:16
container_name: postgres
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
environment:

View File

@@ -1,6 +0,0 @@
{
"$schema": "https://github.com/toeverything/affine/releases/latest/download/config.schema.json",
"server": {
"name": "AFFiNE Self Hosted Server"
}
}

File diff suppressed because it is too large Load Diff

1
.github/CODEOWNERS vendored
View File

@@ -1,2 +1 @@
/blocksuite/ @toeverything/blocksuite-core
/packages/frontend/core/src/blocksuite @toeverything/blocksuite-core

View File

@@ -1,6 +1,6 @@
name: Bug Report
description: File a bug report
title: '[Bug]: '
title: "\u200b"
labels: ['bug']
body:
- type: markdown
@@ -18,26 +18,20 @@ body:
validations:
required: true
- type: dropdown
id: distribution
id: version
attributes:
label: Distribution version
description: What distribution of AFFiNE are you using?
description: What version of AFFiNE are you using?
options:
- macOS x64 (Intel)
- macOS ARM 64 (Apple Silicon)
- Windows x64
- Linux
- Web (https://app.affine.pro)
- Beta Web (https://insider.affine.pro)
- Canary Web (https://affine.fail)
- Web (app.affine.pro)
- Web (affine.fail)
- Web (insider.affine.pro)
validations:
required: true
- type: input
id: version
attributes:
label: App Version
description: What version of AFFiNE are you using?
placeholder: (You can find AFFiNE version in [About AFFiNE] setting panel)
- type: dropdown
id: browsers
attributes:
@@ -57,11 +51,6 @@ body:
If you are self-hosting, please check the box and provide information about your setup.
options:
- label: 'Yes'
- type: input
id: selfhost-version
attributes:
label: Self-hosting Version
description: What version of AFFiNE are you selfhosting?
- type: textarea
id: logs
attributes:

View File

@@ -1,8 +1,7 @@
name: Feature Request
description: Suggest a feature or improvement
title: '[Feature Request]: '
title: "\u200b"
labels: ['feat', 'story']
assignees: ['hwangdev97']
body:
- type: markdown
attributes:

View File

@@ -44,14 +44,12 @@ runs:
RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
- name: Set CC
if: ${{ contains(inputs.target, 'linux') && inputs.no-build != 'true' }}
if: ${{ contains(inputs.target, 'linux') && inputs.package != '@affine/native' && inputs.no-build != 'true' }}
working-directory: ${{ env.DEV_DRIVE_WORKSPACE || github.workspace }}
shell: bash
# https://github.com/tree-sitter/tree-sitter/issues/4186
# pass -D_BSD_SOURCE to clang to fix the tree-sitter build issue
run: |
echo "CC=clang -D_BSD_SOURCE" >> "$GITHUB_ENV"
echo "TARGET_CC=clang -D_BSD_SOURCE" >> "$GITHUB_ENV"
echo "CC=clang" >> "$GITHUB_ENV"
echo "TARGET_CC=clang" >> "$GITHUB_ENV"
- name: Cache cargo
uses: Swatinem/rust-cache@v2
@@ -84,7 +82,7 @@ runs:
shell: bash
if: ${{ runner.os == 'Windows' && inputs.no-build != 'true' }}
run: |
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }}
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
env:
DEBUG: 'napi:*'
CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo

View File

@@ -5,16 +5,27 @@ inputs:
description: 'Script to run'
default: 'yarn affine @affine-test/affine-cloud-copilot e2e --forbid-only'
required: false
openai-key:
description: 'OpenAI secret key'
required: true
fal-key:
description: 'Fal secret key'
required: true
runs:
using: 'composite'
steps:
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- name: Server Copilot E2E Test
shell: bash
run: ${{ inputs.script }}
env:
COPILOT: true
DEV_SERVER_URL: http://localhost:8080
COPILOT_OPENAI_API_KEY: ${{ inputs.openai-key }}
COPILOT_FAL_API_KEY: ${{ inputs.fal-key }}
- name: Upload test results
if: ${{ failure() }}

View File

@@ -10,17 +10,30 @@ const {
DATABASE_USERNAME,
DATABASE_PASSWORD,
DATABASE_NAME,
GCLOUD_CONNECTION_NAME,
R2_ACCOUNT_ID,
R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY,
CAPTCHA_TURNSTILE_SECRET,
METRICS_CUSTOMER_IO_TOKEN,
COPILOT_OPENAI_API_KEY,
COPILOT_FAL_API_KEY,
COPILOT_UNSPLASH_API_KEY,
MAILER_SENDER,
MAILER_USER,
MAILER_PASSWORD,
AFFINE_GOOGLE_CLIENT_ID,
AFFINE_GOOGLE_CLIENT_SECRET,
CLOUD_SQL_IAM_ACCOUNT,
APP_IAM_ACCOUNT,
REDIS_SERVER_HOST,
REDIS_SERVER_PASSWORD,
GCLOUD_CONNECTION_NAME,
GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT,
REDIS_HOST,
REDIS_PASSWORD,
STRIPE_API_KEY,
STRIPE_WEBHOOK_KEY,
STATIC_IP_NAME,
AFFINE_INDEXER_SEARCH_PROVIDER,
AFFINE_INDEXER_SEARCH_ENDPOINT,
AFFINE_INDEXER_SEARCH_API_KEY,
} = process.env;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const buildType = BUILD_TYPE || 'canary';
const isProduction = buildType === 'stable';
@@ -33,21 +46,18 @@ const replicaConfig = {
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 3,
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 3,
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 3,
doc: Number(process.env.PRODUCTION_DOC_REPLICA) || 3,
},
beta: {
web: 2,
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 2,
sync: Number(process.env.BETA_SYNC_REPLICA) || 2,
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 2,
doc: Number(process.env.BETA_DOC_REPLICA) || 2,
},
canary: {
web: 2,
graphql: 2,
sync: 2,
renderer: 2,
doc: 2,
},
};
@@ -56,14 +66,12 @@ const cpuConfig = {
web: '300m',
graphql: '1',
sync: '1',
doc: '1',
renderer: '300m',
},
canary: {
web: '300m',
graphql: '1',
sync: '1',
doc: '1',
renderer: '300m',
},
};
@@ -74,37 +82,27 @@ const createHelmCommand = ({ isDryRun }) => {
const redisAndPostgres =
isProduction || isBeta || isInternal
? [
`--set cloud-sql-proxy.enabled=true`,
`--set-string cloud-sql-proxy.database.connectionName="${GCLOUD_CONNECTION_NAME}"`,
`--set-string global.database.host=${DATABASE_URL}`,
`--set-string global.database.url=${DATABASE_URL}`,
`--set-string global.database.user=${DATABASE_USERNAME}`,
`--set-string global.database.password=${DATABASE_PASSWORD}`,
`--set-string global.database.name=${DATABASE_NAME}`,
`--set-string global.redis.host="${REDIS_SERVER_HOST}"`,
`--set-string global.redis.password="${REDIS_SERVER_PASSWORD}"`,
`--set global.database.gcloud.enabled=true`,
`--set-string global.database.gcloud.connectionName="${GCLOUD_CONNECTION_NAME}"`,
`--set-string global.database.gcloud.cloudSqlInternal="${GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT}"`,
`--set-string global.redis.host="${REDIS_HOST}"`,
`--set-string global.redis.password="${REDIS_PASSWORD}"`,
]
: [];
const indexerOptions = [
`--set-string global.indexer.provider="${AFFINE_INDEXER_SEARCH_PROVIDER}"`,
`--set-string global.indexer.endpoint="${AFFINE_INDEXER_SEARCH_ENDPOINT}"`,
`--set-string global.indexer.apiKey="${AFFINE_INDEXER_SEARCH_API_KEY}"`,
];
const serviceAnnotations = [
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json sync.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json doc.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
].concat(
const serviceAnnotations =
isProduction || isBeta || isInternal
? [
`--set-json web.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json graphql.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json sync.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json cloud-sql-proxy.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }"`,
`--set-json cloud-sql-proxy.nodeSelector="{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }"`,
`--set-json web.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
`--set-json graphql.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
`--set-json sync.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
`--set-json cloud-sql-proxy.serviceAccount.annotations=\"{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }\"`,
`--set-json cloud-sql-proxy.nodeSelector=\"{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }\"`,
]
: []
);
: [];
const cpu = cpuConfig[buildType];
const resources = cpu
@@ -112,7 +110,6 @@ const createHelmCommand = ({ isDryRun }) => {
`--set web.resources.requests.cpu="${cpu.web}"`,
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
`--set sync.resources.requests.cpu="${cpu.sync}"`,
`--set doc.resources.requests.cpu="${cpu.doc}"`,
]
: [];
@@ -125,33 +122,50 @@ const createHelmCommand = ({ isDryRun }) => {
: isInternal
? 'internal'
: 'dev';
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const host = DEPLOY_HOST || CANARY_DEPLOY_HOST;
const deployCommand = [
`helm upgrade --install affine .github/helm/affine`,
`--namespace ${namespace}`,
`--set-string global.deployment.type="affine"`,
`--set-string global.deployment.platform="gcp"`,
`--set-string global.app.buildType="${buildType}"`,
`--set global.ingress.enabled=true`,
`--set-json global.ingress.annotations="{ \\"kubernetes.io/ingress.class\\": \\"gce\\", \\"kubernetes.io/ingress.allow-http\\": \\"true\\", \\"kubernetes.io/ingress.global-static-ip-name\\": \\"${STATIC_IP_NAME}\\" }"`,
`--set-json global.ingress.annotations=\"{ \\"kubernetes.io/ingress.class\\": \\"gce\\", \\"kubernetes.io/ingress.allow-http\\": \\"true\\", \\"kubernetes.io/ingress.global-static-ip-name\\": \\"${STATIC_IP_NAME}\\" }\"`,
`--set-string global.ingress.host="${host}"`,
`--set global.objectStorage.r2.enabled=true`,
`--set-string global.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
`--set-string global.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,
`--set-string global.objectStorage.r2.secretAccessKey="${R2_SECRET_ACCESS_KEY}"`,
`--set-string global.version="${APP_VERSION}"`,
...redisAndPostgres,
...indexerOptions,
`--set web.replicaCount=${replica.web}`,
`--set-string web.image.tag="${imageTag}"`,
`--set graphql.replicaCount=${replica.graphql}`,
`--set-string graphql.image.tag="${imageTag}"`,
`--set graphql.app.host=${host}`,
`--set graphql.app.captcha.enabled=true`,
`--set-string graphql.app.captcha.turnstile.secret="${CAPTCHA_TURNSTILE_SECRET}"`,
`--set graphql.app.copilot.enabled=true`,
`--set-string graphql.app.copilot.openai.key="${COPILOT_OPENAI_API_KEY}"`,
`--set-string graphql.app.copilot.fal.key="${COPILOT_FAL_API_KEY}"`,
`--set-string graphql.app.copilot.unsplash.key="${COPILOT_UNSPLASH_API_KEY}"`,
`--set-string graphql.app.mailer.sender="${MAILER_SENDER}"`,
`--set-string graphql.app.mailer.user="${MAILER_USER}"`,
`--set-string graphql.app.mailer.password="${MAILER_PASSWORD}"`,
`--set-string graphql.app.oauth.google.enabled=true`,
`--set-string graphql.app.oauth.google.clientId="${AFFINE_GOOGLE_CLIENT_ID}"`,
`--set-string graphql.app.oauth.google.clientSecret="${AFFINE_GOOGLE_CLIENT_SECRET}"`,
`--set-string graphql.app.payment.stripe.apiKey="${STRIPE_API_KEY}"`,
`--set-string graphql.app.payment.stripe.webhookKey="${STRIPE_WEBHOOK_KEY}"`,
`--set graphql.app.metrics.enabled=true`,
`--set-string graphql.app.metrics.customerIo.token="${METRICS_CUSTOMER_IO_TOKEN}"`,
`--set graphql.app.experimental.enableJwstCodec=${namespace === 'dev'}`,
`--set graphql.app.features.earlyAccessPreview=false`,
`--set graphql.app.features.syncClientVersionCheck=true`,
`--set sync.replicaCount=${replica.sync}`,
`--set-string sync.image.tag="${imageTag}"`,
`--set-string renderer.image.tag="${imageTag}"`,
`--set renderer.app.host=${host}`,
`--set renderer.replicaCount=${replica.renderer}`,
`--set-string doc.image.tag="${imageTag}"`,
`--set doc.app.host=${host}`,
`--set doc.replicaCount=${replica.doc}`,
...serviceAnnotations,
...resources,
`--timeout 10m`,

View File

@@ -4,11 +4,6 @@ description: 'Prepare Server Test Environment'
runs:
using: 'composite'
steps:
- name: Bundle @affine/reader
shell: bash
run: |
yarn affine @affine/reader build
- name: Initialize database
shell: bash
run: |
@@ -24,12 +19,5 @@ runs:
NODE_ENV: test
run: |
yarn affine @affine/server prisma generate
yarn affine @affine/server prisma migrate deploy
yarn affine @affine/server prisma db push
yarn affine @affine/server data-migration run
- name: Import config
shell: bash
env:
DEFAULT_CONFIG: '{}'
run: |
printf '%s\n' "${SERVER_CONFIG:-$DEFAULT_CONFIG}" > ./packages/backend/server/config.json

View File

@@ -13,10 +13,6 @@ inputs:
description: 'Run the install step for Playwright.'
required: false
default: 'false'
playwright-platform:
description: 'The platform to install Playwright for.'
required: false
default: 'chromium,webkit'
electron-install:
description: 'Download the Electron binary'
required: false
@@ -131,6 +127,7 @@ runs:
- name: yarn install
if: ${{ inputs.package-install == 'true' }}
continue-on-error: true
shell: bash
working-directory: ${{ steps.workspace-path.outputs.workspace_path }}
run: yarn ${{ inputs.extra-flags }}
@@ -176,7 +173,7 @@ runs:
- name: Install Playwright's dependencies
shell: bash
if: inputs.playwright-install == 'true'
run: yarn playwright install --with-deps $(echo "${{ inputs.playwright-platform }}" | tr ',' ' ')
run: yarn playwright install --with-deps chromium webkit
working-directory: ${{ steps.workspace-path.outputs.workspace_path }}
env:
PLAYWRIGHT_BROWSERS_PATH: ${{ steps.workspace-path.outputs.workspace_path }}/node_modules/.cache/ms-playwright

View File

@@ -1,4 +1,4 @@
FROM node:22-bookworm-slim
FROM node:20-bookworm-slim
COPY ./packages/backend/server /app
COPY ./packages/frontend/apps/web/dist /app/static
@@ -10,4 +10,4 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends openssl && \
rm -rf /var/lib/apt/lists/*
CMD ["node", "./dist/main.js"]
CMD ["node", "--import", "./scripts/register.js", "./dist/index.js"]

View File

@@ -0,0 +1,60 @@
services:
affine:
image: ghcr.io/toeverything/affine-graphql:stable
container_name: affine_selfhosted
command:
['sh', '-c', 'node ./scripts/self-host-predeploy && node ./dist/index.js']
ports:
- '3010:3010'
- '5555:5555'
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
volumes:
# custom configurations
- ~/.affine/self-host/config:/root/.affine/config
# blob storage
- ~/.affine/self-host/storage:/root/.affine/storage
logging:
driver: 'json-file'
options:
max-size: '1000m'
restart: unless-stopped
environment:
- NODE_OPTIONS="--import=./scripts/register.js"
- AFFINE_CONFIG_PATH=/root/.affine/config
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgres://affine:affine@postgres:5432/affine
- NODE_ENV=production
# Telemetry allows us to collect data on how you use the affine. This data will helps us improve the app and provide better features.
# Uncomment next line if you wish to quit telemetry.
# - TELEMETRY_ENABLE=false
redis:
image: redis
container_name: affine_redis
restart: unless-stopped
volumes:
- ~/.affine/self-host/redis:/data
healthcheck:
test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']
interval: 10s
timeout: 5s
retries: 5
postgres:
image: postgres:16
container_name: affine_postgres
restart: unless-stopped
volumes:
- ~/.affine/self-host/postgres:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U affine']
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: affine
POSTGRES_PASSWORD: affine
POSTGRES_DB: affine
PGDATA: /var/lib/postgresql/data/pgdata

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.21.0"
appVersion: "0.19.0"

View File

@@ -1,11 +0,0 @@
apiVersion: v2
name: doc
description: AFFiNE doc server
type: application
version: 0.0.0
appVersion: "0.20.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0
repository: "file://../gcloud-sql-proxy"
condition: .global.database.gcloud.enabled

View File

@@ -1,16 +0,0 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "doc.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "doc.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "doc.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "doc.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@@ -1,63 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "doc.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "doc.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "doc.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "doc.labels" -}}
helm.sh/chart: {{ include "doc.chart" . }}
{{ include "doc.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
monitoring: enabled
{{- end }}
{{/*
Selector labels
*/}}
{{- define "doc.selectorLabels" -}}
app.kubernetes.io/name: {{ include "doc.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "doc.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "doc.fullname" .) .Values.global.docService.name }}
{{- else }}
{{- default "default" .Values.global.docService.name }}
{{- end }}
{{- end }}

View File

@@ -1,118 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "doc.fullname" . }}
labels:
{{- include "doc.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "doc.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "doc.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "doc.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "--max-old-space-size=4096"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
- name: SERVER_FLAVOR
value: "doc"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_ENABLED
value: "true"
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
value: "{{ .Values.global.redis.port }}"
- name: REDIS_SERVER_USER
value: "{{ .Values.global.redis.username }}"
- name: REDIS_SERVER_PASSWORD
valueFrom:
secretKeyRef:
name: redis
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.global.docService.port }}"
- name: AFFINE_SERVER_SUB_PATH
value: "{{ .Values.app.path }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
ports:
- name: http
containerPort: {{ .Values.global.docService.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
timeoutSeconds: {{ .Values.probe.timeoutSeconds }}
readinessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
timeoutSeconds: {{ .Values.probe.timeoutSeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "doc.fullname" . }}
labels:
{{- include "doc.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.global.docService.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "doc.selectorLabels" . | nindent 4 }}

View File

@@ -1,12 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "doc.serviceAccountName" . }}
labels:
{{- include "doc.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "doc.fullname" . }}-test-connection"
labels:
{{- include "doc.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "doc.fullname" . }}:{{ .Values.global.docService.port }}']
restartPolicy: Never

View File

@@ -1,43 +0,0 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine-graphql
pullPolicy: IfNotPresent
tag: ''
imagePullSecrets: []
nameOverride: ''
fullnameOverride: ''
# map to NODE_ENV environment variable
env: 'production'
app:
# AFFINE_SERVER_SUB_PATH
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
https: true
copilot:
enabled: false
secretName: copilot
openai:
key: ''
serviceAccount:
create: true
annotations: {}
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
requests:
cpu: '1'
memory: 4Gi
probe:
initialDelaySeconds: 20
timeoutSeconds: 5
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -1,4 +1,4 @@
{{- if .Values.enabled -}}
{{- if .Values.global.database.gcloud.enabled -}}
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "gcloud-sql-proxy.fullname" . }})

View File

@@ -1,4 +1,4 @@
{{- if .Values.enabled -}}
{{- if .Values.global.database.gcloud.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -42,7 +42,7 @@ spec:
- "0.0.0.0"
- "--structured-logs"
- "--auto-iam-authn"
- "{{ .Values.database.connectionName }}"
- "{{ .Values.global.database.gcloud.connectionName }}"
env:
# Enable HTTP healthchecks on port 9801. This enables /liveness,
# /readiness and /startup health check endpoints. Allow connections
@@ -56,7 +56,7 @@ spec:
value: 0.0.0.0
ports:
- name: cloud-sql-proxy
containerPort: {{ .Values.service.port }}
containerPort: {{ .Values.global.database.gcloud.proxyPort }}
protocol: TCP
- containerPort: 9801
protocol: TCP

View File

@@ -1,4 +1,4 @@
{{- if .Values.enabled -}}
{{- if .Values.global.database.gcloud.enabled -}}
apiVersion: v1
kind: Service
metadata:

View File

@@ -1,4 +1,4 @@
{{- if .Values.enabled -}}
{{- if .Values.global.database.gcloud.enabled -}}
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount

View File

@@ -1,4 +1,4 @@
{{- if .Values.enabled -}}
{{- if .Values.global.database.gcloud.enabled -}}
apiVersion: v1
kind: Pod
metadata:

View File

@@ -1,7 +1,4 @@
replicaCount: 3
enabled: false
database:
connectionName: ""
image:
# the tag is defined as chart appVersion.

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.21.0"
appVersion: "0.19.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -0,0 +1,9 @@
{{- if .Values.app.captcha.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.captcha.secretName }}"
type: Opaque
data:
turnstileSecret: {{ .Values.app.captcha.turnstile.secret | b64enc }}
{{- end }}

View File

@@ -0,0 +1,11 @@
{{- if .Values.app.copilot.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.copilot.secretName }}"
type: Opaque
data:
openaiSecret: {{ .Values.app.copilot.openai.key | b64enc }}
falSecret: {{ .Values.app.copilot.fal.key | b64enc }}
unsplashSecret: {{ .Values.app.copilot.unsplash.key | b64enc }}
{{- end }}

View File

@@ -36,13 +36,11 @@ spec:
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "--max-old-space-size=2048"
value: "--max-old-space-size=4096"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
value: "affine"
- name: SERVER_FLAVOR
value: "graphql"
- name: AFFINE_ENV
@@ -53,7 +51,9 @@ spec:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_ENABLED
value: "true"
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
@@ -67,15 +67,6 @@ spec:
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_SUB_PATH
@@ -84,8 +75,127 @@ spec:
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
- name: DOC_SERVICE_ENDPOINT
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
- name: ENABLE_R2_OBJECT_STORAGE
value: "{{ .Values.global.objectStorage.r2.enabled }}"
- name: FEATURES_EARLY_ACCESS_PREVIEW
value: "{{ .Values.app.features.earlyAccessPreview }}"
- name: FEATURES_SYNC_CLIENT_VERSION_CHECK
value: "{{ .Values.app.features.syncClientVersionCheck }}"
- name: MAILER_HOST
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: host
- name: MAILER_PORT
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: port
- name: MAILER_USER
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: user
- name: MAILER_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: password
- name: MAILER_SENDER
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: sender
- name: STRIPE_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.payment.stripe.secretName }}"
key: stripeAPIKey
- name: STRIPE_WEBHOOK_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.payment.stripe.secretName }}"
key: stripeWebhookKey
- name: DOC_MERGE_INTERVAL
value: "{{ .Values.app.doc.mergeInterval }}"
{{ if .Values.app.experimental.enableJwstCodec }}
- name: DOC_MERGE_USE_JWST_CODEC
value: "true"
{{ end }}
{{ if .Values.global.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: secretAccessKey
{{ end }}
{{ if .Values.app.captcha.enabled }}
- name: CAPTCHA_TURNSTILE_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.captcha.secretName }}"
key: turnstileSecret
{{ end }}
{{ if .Values.app.copilot.enabled }}
- name: COPILOT_OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: openaiSecret
- name: COPILOT_FAL_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: falSecret
- name: COPILOT_UNSPLASH_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: unsplashSecret
{{ end }}
{{ if .Values.app.oauth.google.enabled }}
- name: OAUTH_GOOGLE_ENABLED
value: "true"
- name: OAUTH_GOOGLE_CLIENT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.google.secretName }}"
key: clientId
- name: OAUTH_GOOGLE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.google.secretName }}"
key: clientSecret
{{ end }}
{{ if .Values.app.oauth.github.enabled }}
- name: OAUTH_GITHUB_CLIENT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.github.secretName }}"
key: clientId
- name: OAUTH_GITHUB_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.oauth.github.secretName }}"
key: clientSecret
{{ end }}
{{ if .Values.app.metrics.enabled }}
- name: METRICS_CUSTOMER_IO_TOKEN
valueFrom:
secretKeyRef:
name: "{{ .Values.app.metrics.secretName }}"
key: customerIoSecret
{{ end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}

View File

@@ -0,0 +1,13 @@
{{- if .Values.app.mailer.secretName -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.mailer.secretName }}"
type: Opaque
data:
host: "{{ .Values.app.mailer.host | b64enc }}"
port: "{{ .Values.app.mailer.port | b64enc }}"
user: "{{ .Values.app.mailer.user | b64enc }}"
password: "{{ .Values.app.mailer.password | b64enc }}"
sender: "{{ .Values.app.mailer.sender | b64enc }}"
{{- end }}

View File

@@ -0,0 +1,9 @@
{{- if .Values.app.metrics.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.metrics.secretName }}"
type: Opaque
data:
customerIoSecret: {{ .Values.app.metrics.customerIo.token | b64enc }}
{{- end }}

View File

@@ -23,36 +23,37 @@ spec:
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
value: "affine"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
{{ if not .Values.global.database.gcloud.enabled }}
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
value: "{{ .Values.global.redis.port }}"
- name: REDIS_SERVER_USER
value: "{{ .Values.global.redis.username }}"
- name: REDIS_SERVER_PASSWORD
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
{{ end }}
{{ if .Values.global.database.gcloud.enabled }}
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.gcloud.cloudSqlInternal }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
{{ end }}
{{ if .Values.global.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: redis
key: redis-password
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: secretAccessKey
{{ end }}
resources:
requests:
cpu: '100m'

View File

@@ -0,0 +1,21 @@
{{- if .Values.app.oauth.google.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.oauth.google.secretName }}"
type: Opaque
data:
clientId: "{{ .Values.app.oauth.google.clientId | b64enc }}"
clientSecret: "{{ .Values.app.oauth.google.clientSecret | b64enc }}"
{{- end }}
---
{{- if .Values.app.oauth.github.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.oauth.github.secretName }}"
type: Opaque
data:
clientId: "{{ .Values.app.oauth.github.clientId | b64enc }}"
clientSecret: "{{ .Values.app.oauth.github.clientSecret | b64enc }}"
{{- end }}

View File

@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.payment.stripe.secretName }}"
type: Opaque
data:
stripeAPIKey: "{{ .Values.app.payment.stripe.apiKey | b64enc }}"
stripeWebhookKey: "{{ .Values.app.payment.stripe.webhookKey | b64enc }}"

View File

@@ -10,12 +10,57 @@ fullnameOverride: ''
# map to NODE_ENV environment variable
env: 'production'
app:
experimental:
enableJwstCodec: true
# AFFINE_SERVER_SUB_PATH
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
https: true
doc:
mergeInterval: "3000"
captcha:
enabled: false
secretName: captcha
turnstile:
secret: ''
copilot:
enabled: false
secretName: copilot
openai:
key: ''
oauth:
google:
enabled: false
secretName: oauth-google
clientId: ''
clientSecret: ''
github:
enabled: false
secretName: oauth-github
clientId: ''
clientSecret: ''
mailer:
secretName: 'mailer'
host: 'smtp.gmail.com'
port: '465'
user: ''
password: ''
sender: 'noreply@toeverything.info'
metrics:
enabled: false
secretName: 'metrics'
customerIo:
token: ''
payment:
stripe:
secretName: 'stripe'
apiKey: ''
webhookKey: ''
features:
earlyAccessPreview: false
syncClientVersionCheck: false
serviceAccount:
create: true
annotations: {}
@@ -28,8 +73,8 @@ podSecurityContext:
resources:
requests:
cpu: '2'
memory: 2Gi
cpu: '4'
memory: 4Gi
probe:
initialDelaySeconds: 20

View File

@@ -36,13 +36,11 @@ spec:
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "--max-old-space-size=2048"
value: "--max-old-space-size=4096"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
value: "affine"
- name: SERVER_FLAVOR
value: "renderer"
- name: AFFINE_ENV
@@ -53,7 +51,7 @@ spec:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_ENABLED
value: "true"
- name: REDIS_SERVER_HOST
@@ -69,15 +67,6 @@ spec:
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_SUB_PATH
@@ -86,8 +75,25 @@ spec:
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
- name: DOC_SERVICE_ENDPOINT
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
- name: ENABLE_R2_OBJECT_STORAGE
value: "{{ .Values.global.objectStorage.r2.enabled }}"
{{ if .Values.global.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: secretAccessKey
{{ end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}

View File

@@ -27,8 +27,8 @@ podSecurityContext:
resources:
requests:
cpu: '1'
memory: 2Gi
cpu: '4'
memory: 4Gi
probe:
initialDelaySeconds: 20

View File

@@ -3,7 +3,7 @@ name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.21.0"
appVersion: "0.19.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -42,9 +42,7 @@ spec:
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
value: "affine"
- name: SERVER_FLAVOR
value: "sync"
- name: AFFINE_ENV
@@ -55,7 +53,9 @@ spec:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_ENABLED
value: "true"
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
@@ -69,21 +69,10 @@ spec:
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: DOC_SERVICE_ENDPOINT
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
ports:
- name: http
containerPort: {{ .Values.service.port }}

View File

@@ -24,11 +24,11 @@ podSecurityContext:
resources:
limits:
cpu: '4'
memory: 8Gi
requests:
cpu: '2'
memory: 4Gi
requests:
cpu: '1'
memory: 2Gi
probe:
initialDelaySeconds: 20

View File

@@ -1,13 +0,0 @@
{{- if .Values.global.indexer.apiKey -}}
apiVersion: v1
kind: Secret
metadata:
name: indexer
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-2"
"helm.sh/hook-delete-policy": before-hook-creation
type: Opaque
data:
indexer-apiKey: {{ .Values.global.indexer.apiKey | b64enc }}
{{- end }}

View File

@@ -1,13 +0,0 @@
{{- if eq .Values.global.deployment.platform "gcp" -}}
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: "{{ .Release.Name }}-monitoring"
spec:
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
endpoints:
- port: 9464
interval: 30s
{{- end }}

View File

@@ -0,0 +1,11 @@
{{- if .Values.global.objectStorage.r2.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
type: Opaque
data:
accountId: {{ .Values.global.objectStorage.r2.accountId | b64enc }}
accessKeyId: {{ .Values.global.objectStorage.r2.accessKeyId | b64enc }}
secretAccessKey: {{ .Values.global.objectStorage.r2.secretAccessKey | b64enc }}
{{- end }}

View File

@@ -11,28 +11,34 @@ global:
privateKey: ''
database:
user: 'postgres'
host: 'pg-postgresql'
url: 'pg-postgresql'
port: '5432'
name: 'affine'
password: ''
gcloud:
enabled: false
# use for migration
cloudSqlInternal: ''
connectionName: ''
serviceAccount: ''
cloudProxyReplicas: 3
proxyPort: '5432'
redis:
enabled: true
host: 'redis-master'
port: '6379'
username: ''
password: ''
database: 0
indexer:
provider: ''
endpoint: ''
username: ''
password: ''
docService:
name: 'affine-doc'
port: 3020
deployment:
# change to 'selfhosted' and 'unknown' if this chart is ready to be used for selfhosted deployment
type: 'affine'
platform: 'gcp'
objectStorage:
r2:
enabled: false
secretName: r2
accountId: ''
accessKeyId: ''
secretAccessKey: ''
gke:
enabled: true
graphql:
service:
@@ -55,12 +61,6 @@ renderer:
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
doc:
service:
type: ClusterIP
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
web:
service:
type: ClusterIP

View File

@@ -1,6 +0,0 @@
FROM pgvector/pgvector:pg15 AS builder
FROM bitnami/postgresql:15
COPY --from=builder /usr/lib/postgresql/15/lib/vector.so /opt/bitnami/postgresql/lib/
COPY --from=builder /usr/share/postgresql/15/extension/vector* /opt/bitnami/postgresql/share/extension/

35
.github/renovate.json vendored
View File

@@ -21,34 +21,16 @@
"groupName": "oxlint"
},
{
"groupName": "all non-major rust dependencies",
"groupSlug": "all-minor-patch",
"matchUpdateTypes": ["minor", "patch"],
"matchManagers": ["cargo"]
},
{
"groupName": "all non-major npm dependencies",
"groupSlug": "all-minor-patch",
"matchUpdateTypes": ["minor", "patch"],
"matchManagers": ["npm"],
"matchPackageNames": ["*", "!/^@blocksuite//", "!/oxlint/"]
"groupName": "blocksuite",
"rangeStrategy": "replace",
"changelogUrl": "https://github.com/toeverything/blocksuite/blob/master/packages/blocks/CHANGELOG.md",
"matchPackageNames": ["/^@blocksuite/", "!@blocksuite/icons"]
},
{
"groupName": "all non-major dependencies",
"groupSlug": "all-minor-patch",
"matchUpdateTypes": ["minor", "patch"],
"matchManagers": [
"dockerfile",
"github-actions",
"helmv3",
"helm-values",
"gradle-wrapper",
"gradle",
"docker-compose",
"devcontainer",
"cocoapods",
"bundler"
]
"matchPackageNames": ["*", "!/^@blocksuite//", "!/oxlint/"]
},
{
"groupName": "rust toolchain",
@@ -58,13 +40,6 @@
{
"groupName": "nestjs",
"matchPackageNames": ["/^@nestjs/"]
},
{
"groupName": "opentelemetry",
"matchPackageNames": [
"/^@opentelemetry/",
"/^@google-cloud\/opentelemetry-/"
]
}
],
"commitMessagePrefix": "chore: ",

View File

@@ -13,6 +13,28 @@ permissions:
packages: 'write'
jobs:
build-server:
name: Build Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
@@ -113,13 +135,12 @@ jobs:
build-server-native:
name: Build Server native - ${{ matrix.targets.name }}
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
strategy:
fail-fast: false
matrix:
targets:
- name: x86_64-unknown-linux-gnu
file: server-native.x64.node
file: server-native.node
- name: aarch64-unknown-linux-gnu
file: server-native.arm64.node
- name: armv7-unknown-linux-gnueabihf
@@ -137,54 +158,14 @@ jobs:
extra-flags: workspaces focus @affine/server-native
- name: Build Rust
uses: ./.github/actions/build-rust
env:
AFFINE_PRO_PUBLIC_KEY: ${{ secrets.AFFINE_PRO_PUBLIC_KEY }}
AFFINE_PRO_LICENSE_AES_KEY: ${{ secrets.AFFINE_PRO_LICENSE_AES_KEY }}
with:
target: ${{ matrix.targets.name }}
package: '@affine/server-native'
- name: Rename ${{ matrix.targets.file }}
run: |
mv ./packages/backend/native/server-native.node ./packages/backend/native/${{ matrix.targets.file }}
- name: Upload ${{ matrix.targets.file }}
uses: actions/upload-artifact@v4
with:
name: server-native-${{ matrix.targets.file }}
path: ./packages/backend/native/${{ matrix.targets.file }}
if-no-files-found: error
build-server:
name: Build Server
runs-on: ubuntu-latest
needs:
- build-server-native
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server @types/affine__env
- name: Download server-native
uses: actions/download-artifact@v4
with:
pattern: server-native-*
merge-multiple: true
path: ./packages/backend/native
- name: List server-native files
run: ls -alh ./packages/backend/native
- name: Build @affine/reader
run: yarn workspace @affine/reader build
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
name: ${{ matrix.targets.file }}
path: ./packages/backend/native/server-native.node
if-no-files-found: error
build-images:
@@ -195,6 +176,7 @@ jobs:
- build-web
- build-mobile
- build-admin
- build-server-native
steps:
- uses: actions/checkout@v4
- name: Download server dist
@@ -202,6 +184,25 @@ jobs:
with:
name: server-dist
path: ./packages/backend/server/dist
- name: Download server-native.node
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Download server-native.node arm64
uses: actions/download-artifact@v4
with:
name: server-native.arm64.node
path: ./packages/backend/native
- name: Download server-native.node arm64
uses: actions/download-artifact@v4
with:
name: server-native.armv7.node
path: .
- name: move server-native files
run: |
mv ./packages/backend/native/server-native.node ./packages/backend/server/server-native.arm64.node
mv server-native.node ./packages/backend/server/server-native.armv7.node
- name: Setup env
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
@@ -259,9 +260,6 @@ jobs:
- name: Generate Prisma client
run: yarn workspace @affine/server prisma generate
- name: Mv node_modules
run: mv ./node_modules ./packages/backend/server
- name: Setup Version
id: version
uses: ./.github/actions/setup-version

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@ jobs:
REDIS_SERVER_HOST: localhost
services:
postgres:
image: pgvector/pgvector:pg16
image: postgres
env:
POSTGRES_PASSWORD: affine
options: >-
@@ -59,10 +59,6 @@ jobs:
ports:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.3.2
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4
@@ -77,17 +73,17 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/native
path: ./packages/backend/server
- name: Prepare Server Test Environment
env:
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
uses: ./.github/actions/server-test-env
- name: Run server tests
run: yarn affine @affine/server test:copilot:coverage --forbid-only
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
- name: Upload server test coverage results
uses: codecov/codecov-action@v5
@@ -99,7 +95,7 @@ jobs:
fail_ci_if_error: false
copilot-e2e-test:
name: Frontend Copilot E2E Test
name: Server Copilot E2E Test
runs-on: ubuntu-latest
env:
DISTRIBUTION: web
@@ -109,13 +105,13 @@ jobs:
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
shardTotal: [8]
shardIndex: [1, 2, 3]
shardTotal: [3]
needs:
- build-server-native
services:
postgres:
image: pgvector/pgvector:pg16
image: postgres
env:
POSTGRES_PASSWORD: affine
options: >-
@@ -129,10 +125,6 @@ jobs:
image: redis
ports:
- 6379:6379
indexer:
image: manticoresearch/manticore:9.3.2
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4
@@ -147,17 +139,14 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/native
- name: Prepare Server Test Environment
env:
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
uses: ./.github/actions/server-test-env
path: ./packages/backend/server
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
uses: ./.github/actions/copilot-test
with:
script: yarn affine @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
openai-key: ${{ secrets.COPILOT_OPENAI_API_KEY }}
fal-key: ${{ secrets.COPILOT_FAL_API_KEY }}
test-done:
needs:

View File

@@ -92,20 +92,32 @@ jobs:
APP_VERSION: ${{ steps.version.outputs.APP_VERSION }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
CANARY_DEPLOY_HOST: ${{ secrets.CANARY_DEPLOY_HOST }}
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_UNSPLASH_API_KEY: ${{ secrets.COPILOT_UNSPLASH_API_KEY }}
METRICS_CUSTOMER_IO_TOKEN: ${{ secrets.METRICS_CUSTOMER_IO_TOKEN }}
MAILER_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
MAILER_USER: ${{ secrets.OAUTH_EMAIL_LOGIN }}
MAILER_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AFFINE_GOOGLE_CLIENT_ID: ${{ secrets.AFFINE_GOOGLE_CLIENT_ID }}
AFFINE_GOOGLE_CLIENT_SECRET: ${{ secrets.AFFINE_GOOGLE_CLIENT_SECRET }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
GCLOUD_CONNECTION_NAME: ${{ secrets.GCLOUD_CONNECTION_NAME }}
REDIS_SERVER_HOST: ${{ secrets.REDIS_SERVER_HOST }}
REDIS_SERVER_PASSWORD: ${{ secrets.REDIS_SERVER_PASSWORD }}
GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT: ${{ secrets.GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT }}
REDIS_HOST: ${{ secrets.REDIS_HOST }}
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
CLOUD_SQL_IAM_ACCOUNT: ${{ secrets.CLOUD_SQL_IAM_ACCOUNT }}
APP_IAM_ACCOUNT: ${{ secrets.APP_IAM_ACCOUNT }}
STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
STRIPE_WEBHOOK_KEY: ${{ secrets.STRIPE_WEBHOOK_KEY }}
STATIC_IP_NAME: ${{ secrets.STATIC_IP_NAME }}
AFFINE_INDEXER_SEARCH_PROVIDER: ${{ secrets.AFFINE_INDEXER_SEARCH_PROVIDER }}
AFFINE_INDEXER_SEARCH_ENDPOINT: ${{ secrets.AFFINE_INDEXER_SEARCH_ENDPOINT }}
AFFINE_INDEXER_SEARCH_API_KEY: ${{ secrets.AFFINE_INDEXER_SEARCH_API_KEY }}
deploy-done:
needs:
@@ -159,7 +171,7 @@ jobs:
BLOCKSUITE_REPO_PATH: ${{ github.workspace }}/blocksuite
- name: Post Failed event to a Slack channel
id: failed-slack
uses: slackapi/slack-github-action@v2.1.0
uses: slackapi/slack-github-action@v2.0.0
if: ${{ always() && contains(needs.*.result, 'failure') }}
with:
method: chat.postMessage
@@ -174,7 +186,7 @@ jobs:
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy failed `${{ github.event.inputs.flavor }}`>"
- name: Post Cancel event to a Slack channel
id: cancel-slack
uses: slackapi/slack-github-action@v2.1.0
uses: slackapi/slack-github-action@v2.0.0
if: ${{ always() && contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
with:
token: ${{ secrets.SLACK_BOT_TOKEN }}

View File

@@ -73,11 +73,11 @@ jobs:
fail-fast: false
matrix:
spec:
- runner: macos-latest
- runner: macos-14
platform: darwin
arch: x64
target: x86_64-apple-darwin
- runner: macos-latest
- runner: macos-14
platform: darwin
arch: arm64
target: aarch64-apple-darwin
@@ -127,7 +127,7 @@ jobs:
- name: Signing By Apple Developer ID
if: ${{ matrix.spec.platform == 'darwin' }}
uses: apple-actions/import-codesign-certs@v5
uses: apple-actions/import-codesign-certs@v3
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
@@ -142,19 +142,11 @@ jobs:
# some flatpak deps need git protocol.file.allow
git config --global protocol.file.allow always
- name: Remove nbstore node_modules
shell: bash
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: make
run: yarn affine @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
NODE_OPTIONS: --max-old-space-size=14384
- name: signing DMG
if: ${{ matrix.spec.platform == 'darwin' }}
@@ -248,20 +240,11 @@ jobs:
- name: Build Desktop Layers
run: yarn affine @affine/electron build
- name: Remove nbstore node_modules
shell: bash
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: package
run: |
yarn affine @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
run: yarn affine @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
NODE_OPTIONS: --max-old-space-size=14384
- name: get all files to be signed
id: get_files_to_be_signed
@@ -460,7 +443,6 @@ jobs:
run: |
cp ./.docker/selfhost/compose.yml ./release/docker-compose.yml
cp ./.docker/selfhost/.env.example ./release/.env.example
cp ./.docker/selfhost/schema.json ./release/config.schema.json
- name: Generate Release yml
run: |
node ./scripts/generate-release-yml.mjs

View File

@@ -54,7 +54,7 @@ jobs:
build-ios-web:
needs:
- output-env
runs-on: ubuntu-24.04-arm
runs-on: ubuntu-latest
environment: ${{ needs.output-env.outputs.ENVIRONMENT }}
outputs:
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
@@ -85,7 +85,7 @@ jobs:
path: packages/frontend/apps/ios/dist
build-android-web:
runs-on: ubuntu-24.04-arm
runs-on: ubuntu-latest
needs:
- output-env
environment: ${{ needs.output-env.outputs.ENVIRONMENT }}
@@ -118,7 +118,7 @@ jobs:
path: packages/frontend/apps/android/dist
ios:
runs-on: ${{ github.ref_name == 'canary' && 'macos-latest' || 'blaze/macos-14' }}
runs-on: macos-latest
needs:
- build-ios-web
steps:
@@ -139,13 +139,11 @@ jobs:
enableScripts: false
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 16.2
- name: Install Swiftformat
run: brew install swiftformat
xcode-version: latest-stable
- name: Cap sync
run: yarn workspace @affine/ios sync
run: yarn workspace @affine/ios cap sync
- name: Signing By Apple Developer ID
uses: apple-actions/import-codesign-certs@v5
uses: apple-actions/import-codesign-certs@v3
id: import-codesign-certs
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12_MOBILE }}
@@ -156,6 +154,11 @@ jobs:
target: 'aarch64-apple-ios'
package: 'affine_mobile_native'
no-build: 'true'
- name: Build Rust
run: |
brew install swiftformat
cargo build -p affine_mobile_native --lib --release --target aarch64-apple-ios
cargo run -p affine_mobile_native --bin uniffi-bindgen generate --library target/aarch64-apple-ios/release/libaffine_mobile_native.a --language swift --out-dir packages/frontend/apps/ios/App/App/uniffi
- name: Testflight
if: ${{ env.BUILD_TYPE != 'stable' }}
working-directory: packages/frontend/apps/ios/App
@@ -179,23 +182,16 @@ jobs:
- build-android-web
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Download mobile artifact
uses: actions/download-artifact@v4
with:
name: android
path: packages/frontend/apps/android/dist
- name: Load Google Service file
env:
DATA: ${{ secrets.FIREBASE_ANDROID_GOOGLE_SERVICE_JSON }}
run: echo $DATA | base64 -di > packages/frontend/apps/android/App/app/google-services.json
- name: Setup Node.js
uses: ./.github/actions/setup-node
timeout-minutes: 10
with:
extra-flags: workspaces focus @affine/monorepo @affine-tools/cli @affine/android @affine/playstore-auto-bump
extra-flags: workspaces focus @affine/android @affine/playstore-auto-bump
playwright-install: false
electron-install: false
hard-link-nm: false
@@ -210,7 +206,7 @@ jobs:
run: yarn workspace @affine/android cap sync
- uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.12'
- name: Auth gcloud
id: auth
uses: google-github-actions/auth@v2
@@ -224,7 +220,7 @@ jobs:
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
java-version: '17'
cache: 'gradle'
- name: Auto increment version code
id: bump
@@ -235,20 +231,19 @@ jobs:
- name: Build
run: |
echo -n "${{ env.AFFINE_ANDROID_SIGN_KEYSTORE }}" | base64 --decode > packages/frontend/apps/android/affine.keystore
yarn workspace @affine/android cap build android --flavor ${{ env.BUILD_TYPE }} --androidreleasetype AAB
yarn workspace @affine/android cap build android
env:
AFFINE_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_PASSWORD }}
AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD }}
AFFINE_ANDROID_SIGN_KEYSTORE: ${{ secrets.AFFINE_ANDROID_SIGN_KEYSTORE }}
VERSION_NAME: ${{ steps.version.outputs.APP_VERSION }}
- name: Upload to Google Play
uses: r0adkll/upload-google-play@v1
if: ${{ env.BUILD_TARGET == 'distribution' }}
with:
serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }}
packageName: app.affine.pro
releaseName: ${{ steps.version.outputs.APP_VERSION }}
releaseFiles: packages/frontend/apps/android/App/app/build/outputs/bundle/${{ env.BUILD_TYPE }}Release/app-${{ env.BUILD_TYPE }}-release-signed.aab
releaseFiles: packages/frontend/apps/android/App/app/build/outputs/bundle/release/app-release-signed.aab
track: internal
status: draft
existingEditId: ${{ steps.bump.outputs.EDIT_ID }}

View File

@@ -11,6 +11,7 @@ on:
jobs:
synchronize-with-crowdin:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
@@ -20,11 +21,10 @@ jobs:
uses: actions/checkout@v4
- name: Crowdin action
id: crowdin
uses: crowdin/github-action@v2
with:
upload_sources: true
upload_translations: false
upload_translations: true
download_translations: true
auto_approve_imported: true
import_eq_suggestions: true
@@ -40,33 +40,3 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
i18n-codegen:
needs: synchronize-with-crowdin
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: l10n_crowdin_translations
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
full-cache: true
- name: Run i18n codegen
run: yarn affine @affine/i18n build
- name: Commit changes
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "chore(i18n): i18n codegen"
git push origin l10n_crowdin_translations

23
.github/workflows/workers.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Deploy Cloudflare Worker
on:
push:
branches:
- canary
paths:
- tools/workers/**
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
environment: stable
steps:
- uses: actions/checkout@v4
- name: Publish
uses: cloudflare/wrangler-action@v3.13.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: 'tools/workers'
packageManager: 'yarn'

4
.gitignore vendored
View File

@@ -84,7 +84,3 @@ packages/frontend/core/public/static/templates
# script
af
af.cmd
*.resolved
# playwright
storageState.json

2
.nvmrc
View File

@@ -1 +1 @@
22.16.0
20.18.1

View File

@@ -1,7 +1,7 @@
# we will make this file shared by prettier|eslint|oxlint
**/node_modules
.yarn
.github/helm
.github
.vscode
.yarnrc.yml
.docker
@@ -28,15 +28,9 @@ test-results
# per files
tools/cli/src/webpack/error-handler.js
packages/backend/native/index.d.ts
packages/backend/server/src/__tests__/__snapshots__
packages/common/native/fixtures/**
packages/common/graphql/src/graphql/index.ts
packages/frontend/native/index.d.ts
packages/frontend/native/index.js
packages/frontend/apps/android/App/**
packages/frontend/apps/ios/App/**
tests/blocksuite/snapshots
blocksuite/docs/api/**
packages/frontend/admin/src/config.json
**/test-docs.json
**/test-blocks.json
packages/frontend/graphql/src/graphql/index.ts
packages/frontend/graphql/src/schema.ts
packages/frontend/apps/android/App/app/build/**
blocksuite/tests-legacy/snapshots

View File

@@ -7,11 +7,7 @@
"request": "launch",
"runtimeExecutable": "yarn",
"cwd": "${workspaceFolder}",
"runtimeArgs": [
"affine",
"@affine/server",
"dev"
]
"runtimeArgs": ["affine", "@affine/server", "dev"]
},
{
"name": "Lanuch AFFiNE Web",
@@ -19,17 +15,7 @@
"request": "launch",
"runtimeExecutable": "yarn",
"cwd": "${workspaceFolder}",
"runtimeArgs": [
"affine",
"@affine/web",
"dev"
]
},
{
"type": "chrome",
"request": "launch",
"name": "Debug AFFiNE Web",
"url": "http://localhost:8080"
"runtimeArgs": ["affine", "@affine/web", "dev"]
}
]
}
}

View File

@@ -1,36 +0,0 @@
diff --git a/testing-module.d.ts b/testing-module.d.ts
index 0b08dfe24534c605f58f2104255eb2a20c3fb566..8fad3ab267decccca2d4d9a8e208ace2cd811e92 100644
--- a/testing-module.d.ts
+++ b/testing-module.d.ts
@@ -13,6 +13,7 @@ export declare class TestingModule extends NestApplicationContext {
protected readonly graphInspector: GraphInspector;
constructor(container: NestContainer, graphInspector: GraphInspector, contextModule: Module, applicationConfig: ApplicationConfig, scope?: Type<any>[]);
private isHttpServer;
+ useCustomApplicationConstructor(Ctor: Type<INestApplication>): void;
createNestApplication<T extends INestApplication = INestApplication>(httpAdapter: HttpServer | AbstractHttpAdapter, options?: NestApplicationOptions): T;
createNestApplication<T extends INestApplication = INestApplication>(options?: NestApplicationOptions): T;
createNestMicroservice<T extends object>(options: NestMicroserviceOptions & T): INestMicroservice;
diff --git a/testing-module.js b/testing-module.js
index 17957b409b224bc43c7e40a1071cf08061366063..6bc4e8a694fdec02df91e512131ffd70259d8859 100644
--- a/testing-module.js
+++ b/testing-module.js
@@ -15,6 +15,9 @@ class TestingModule extends core_1.NestApplicationContext {
this.applicationConfig = applicationConfig;
this.graphInspector = graphInspector;
}
+ useCustomApplicationConstructor(Ctor) {
+ this.applicationConstructor = Ctor;
+ }
isHttpServer(serverOrOptions) {
return !!(serverOrOptions && serverOrOptions.patch);
}
@@ -24,7 +27,8 @@ class TestingModule extends core_1.NestApplicationContext {
: [this.createHttpAdapter(), serverOrOptions];
this.applyLogger(appOptions);
this.container.setHttpAdapter(httpAdapter);
- const instance = new core_1.NestApplication(this.container, httpAdapter, this.applicationConfig, this.graphInspector, appOptions);
+ const Ctor = this.applicationConstructor || core_1.NestApplication;
+ const instance = new Ctor(this.container, httpAdapter, this.applicationConfig, this.graphInspector, appOptions);
return this.createAdapterProxy(instance, httpAdapter);
}
createNestMicroservice(options) {

934
.yarn/releases/yarn-4.6.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -10,6 +10,6 @@ npmAuthToken: "${NPM_TOKEN:-NONE}"
npmPublishAccess: public
npmRegistryServer: "https://registry.npmjs.org"
npmPublishRegistry: "https://registry.npmjs.org"
yarnPath: .yarn/releases/yarn-4.9.1.cjs
yarnPath: .yarn/releases/yarn-4.6.0.cjs

3167
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,104 +2,47 @@
members = [
"./packages/backend/native",
"./packages/common/native",
"./packages/common/y-octo/core",
"./packages/common/y-octo/node",
"./packages/common/y-octo/utils",
"./packages/frontend/mobile-native",
"./packages/frontend/native",
"./packages/frontend/native/nbstore",
"./packages/frontend/native/schema",
"./packages/frontend/native/sqlite_v1",
]
resolver = "3"
[workspace.package]
edition = "2024"
resolver = "2"
[workspace.dependencies]
affine_common = { path = "./packages/common/native" }
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
ahash = "0.8"
anyhow = "1"
arbitrary = { version = "1.3", features = ["derive"] }
assert-json-diff = "2.0"
async-lock = { version = "3.4.0", features = ["loom"] }
base64-simd = "0.8"
bitvec = "1.0"
block2 = "0.6"
byteorder = "1.5"
chrono = "0.4"
clap = { version = "4.4", features = ["derive"] }
core-foundation = "0.10"
coreaudio-rs = "0.12"
criterion = { version = "0.5", features = ["html_reports"] }
criterion2 = { version = "3", default-features = false }
dispatch2 = "0.3"
docx-parser = { git = "https://github.com/toeverything/docx-parser" }
dotenvy = "0.15"
file-format = { version = "0.26", features = ["reader"] }
homedir = "0.3"
infer = { version = "0.19.0" }
lasso = { version = "0.7", features = ["multi-threaded"] }
lib0 = { version = "0.16", features = ["lib0-serde"] }
libc = "0.2"
log = "0.4"
loom = { version = "0.7", features = ["checkpoint"] }
mimalloc = "0.1"
nanoid = "0.4"
napi = { version = "3.0.0-beta.3", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
napi-build = { version = "2" }
napi-derive = { version = "3.0.0-beta.3" }
nom = "8"
notify = { version = "8", features = ["serde"] }
objc2 = "0.6"
objc2-foundation = "0.3"
once_cell = "1"
ordered-float = "5"
parking_lot = "0.12"
path-ext = "0.1.2"
pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" }
phf = { version = "0.11", features = ["macros"] }
proptest = "1.3"
proptest-derive = "0.5"
rand = "0.9"
rand_chacha = "0.9"
rand_distr = "0.5"
rayon = "1.10"
readability = { version = "0.3.0", default-features = false }
regex = "1.10"
rubato = "0.16"
screencapturekit = "0.3"
serde = "1"
serde_json = "1"
sha3 = "0.10"
smol_str = "0.3"
sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] }
strum_macros = "0.27.0"
symphonia = { version = "0.5", features = ["all", "opt-simd"] }
text-splitter = "0.27"
thiserror = "2"
tiktoken-rs = "0.7"
tokio = "1.45"
tree-sitter = { version = "0.25" }
tree-sitter-c = { version = "0.24" }
tree-sitter-c-sharp = { version = "0.23" }
tree-sitter-cpp = { version = "0.23" }
tree-sitter-go = { version = "0.23" }
tree-sitter-java = { version = "0.23" }
tree-sitter-javascript = { version = "0.23" }
tree-sitter-kotlin-ng = { version = "1.1" }
tree-sitter-python = { version = "0.23" }
tree-sitter-rust = { version = "0.24" }
tree-sitter-scala = { version = "0.23" }
tree-sitter-typescript = { version = "0.23" }
uniffi = "0.29"
url = { version = "2.5" }
uuid = "1.8"
v_htmlescape = "0.15"
y-octo = { path = "./packages/common/y-octo/core" }
y-sync = { version = "0.4" }
yrs = "0.23.0"
affine_common = { path = "./packages/common/native" }
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
anyhow = "1"
base64-simd = "0.8"
chrono = "0.4"
criterion2 = { version = "2", default-features = false }
dashmap = "6"
dotenvy = "0.15"
file-format = { version = "0.26", features = ["reader"] }
mimalloc = "0.1"
napi = { version = "3.0.0-alpha.12", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
napi-build = { version = "2" }
napi-derive = { version = "3.0.0-alpha.12" }
notify = { version = "7", features = ["serde"] }
objc2 = "0.5.2"
objc2-foundation = "0.2.2"
once_cell = "1"
parking_lot = "0.12"
homedir = "0.3"
rand = "0.8"
rayon = "1.10"
serde = "1"
serde_json = "1"
sha3 = "0.10"
sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] }
thiserror = "2"
tiktoken-rs = "0.6"
tokio = "1.37"
uniffi = "0.28"
uuid = "1.8"
v_htmlescape = "0.15"
y-octo = { git = "https://github.com/y-crdt/y-octo.git", branch = "main" }
[profile.dev.package.sqlx-macros]
opt-level = 3
@@ -109,7 +52,3 @@ codegen-units = 1
lto = true
opt-level = 3
strip = "symbols"
# android uniffi bindgen requires symbols
[profile.release.package.affine_mobile_native]
strip = "none"

View File

@@ -21,29 +21,12 @@
<br/>
<br/>
<div align="left" valign="middle">
<a href="https://runblaze.dev">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://www.runblaze.dev/logo_dark.png">
<img align="right" src="https://www.runblaze.dev/logo_light.png" height="102px"/>
</picture>
</a>
<br style="display: none;"/>
_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Apple Silicon macOS and Linux (AMD64 & ARM64) runners for GitHub Actions, greatly reducing our automated build times._
</div>
<br/>
<br/>
<div align="center">
<a href="https://affine.pro">Home Page</a> |
<a href="https://affine.pro/redirect/discord">Discord</a> |
<a href="https://discord.gg/whd5mjYqVw">Discord</a> |
<a href="https://app.affine.pro">Live Demo</a> |
<a href="https://affine.pro/blog/">Blog</a> |
<a href="https://docs.affine.pro/">Documentation</a>
<a href="https://docs.affine.pro/docs/">Documentation</a>
</div>
<br/>
@@ -89,7 +72,7 @@ Star us, and you will receive all release notifications from GitHub without any
**Self-host & Shape your own AFFiNE**
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks are coming soon. More tractions on [Blocksuite](https://blocksuite.io). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/self-host-affine).
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks are coming soon. More tractions on [Blocksuite](https://blocksuite.io). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/docs/self-host-affine).
## Acknowledgement
@@ -144,8 +127,6 @@ AFFiNE now provides pre-built [templates](https://affine.pro/templates) from our
Welcome to the AFFiNE blog section! Here, youll find the latest insights, tips, and guides on how to maximize your experience with AFFiNE and AFFiNE AI, the leading Canvas AI tool for flexible note-taking and creative organization.
- [vision board template](https://affine.pro/blog/8-free-printable-vision-board-templates-examples-2023)
- [ai homework helper](https://affine.pro/blog/ai-homework-helper)
- [vision board maker](https://affine.pro/blog/vision-board-maker)
- [itinerary template](https://affine.pro/blog/free-customized-travel-itinerary-planner-templates)
- [one pager template](https://affine.pro/blog/top-12-one-pager-examples-how-to-create-your-own)
- [cornell notes template](https://affine.pro/blog/the-cornell-notes-template-and-system-learning-tips)
@@ -191,13 +172,11 @@ We would like to express our gratitude to all the individuals who have already c
## Self-Host
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/self-host-affine).
[![Run on ClawCloud](https://raw.githubusercontent.com/ClawCloud/Run-Template/refs/heads/main/Run-on-ClawCloud.svg)](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Daffine)
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/docs/self-host-affine).
## Hiring
Some amazing companies, including AFFiNE, are looking for developers! Are you interested in joining AFFiNE or its partners? Check out our [Discord channel](https://affine.pro/redirect/discord) for some of the latest jobs available.
Some amazing companies, including AFFiNE, are looking for developers! Are you interested in joining AFFiNE or its partners? Check out our Discord channel for some of the latest jobs available.
## Feature Request

View File

@@ -3,288 +3,93 @@
"description": "BlockSuite for Affine",
"type": "module",
"scripts": {
"build": "tsc --build --verbose"
"build": "tsc --build --verbose",
"test:unit": "nx vite:test --run --passWithNoTests",
"test:unit:coverage": "nx vite:test --run --coverage",
"test:e2e": "playwright test"
},
"sideEffects": false,
"keywords": [],
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-attachment": "workspace:*",
"@blocksuite/affine-block-bookmark": "workspace:*",
"@blocksuite/affine-block-callout": "workspace:*",
"@blocksuite/affine-block-code": "workspace:*",
"@blocksuite/affine-block-data-view": "workspace:*",
"@blocksuite/affine-block-database": "workspace:*",
"@blocksuite/affine-block-divider": "workspace:*",
"@blocksuite/affine-block-edgeless-text": "workspace:*",
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-embed-doc": "workspace:*",
"@blocksuite/affine-block-frame": "workspace:*",
"@blocksuite/affine-block-image": "workspace:*",
"@blocksuite/affine-block-latex": "workspace:*",
"@blocksuite/affine-block-list": "workspace:*",
"@blocksuite/affine-block-note": "workspace:*",
"@blocksuite/affine-block-paragraph": "workspace:*",
"@blocksuite/affine-block-root": "workspace:*",
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-block-surface-ref": "workspace:*",
"@blocksuite/affine-block-table": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-foundation": "workspace:*",
"@blocksuite/affine-fragment-adapter-panel": "workspace:*",
"@blocksuite/affine-fragment-doc-title": "workspace:*",
"@blocksuite/affine-fragment-frame-panel": "workspace:*",
"@blocksuite/affine-fragment-outline": "workspace:*",
"@blocksuite/affine-gfx-brush": "workspace:*",
"@blocksuite/affine-gfx-connector": "workspace:*",
"@blocksuite/affine-gfx-group": "workspace:*",
"@blocksuite/affine-gfx-link": "workspace:*",
"@blocksuite/affine-gfx-mindmap": "workspace:*",
"@blocksuite/affine-gfx-note": "workspace:*",
"@blocksuite/affine-gfx-pointer": "workspace:*",
"@blocksuite/affine-gfx-shape": "workspace:*",
"@blocksuite/affine-gfx-template": "workspace:*",
"@blocksuite/affine-gfx-text": "workspace:*",
"@blocksuite/affine-gfx-turbo-renderer": "workspace:*",
"@blocksuite/affine-inline-footnote": "workspace:*",
"@blocksuite/affine-inline-latex": "workspace:*",
"@blocksuite/affine-inline-link": "workspace:*",
"@blocksuite/affine-inline-mention": "workspace:*",
"@blocksuite/affine-inline-preset": "workspace:*",
"@blocksuite/affine-inline-reference": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-drag-handle": "workspace:*",
"@blocksuite/affine-widget-edgeless-auto-connect": "workspace:*",
"@blocksuite/affine-widget-edgeless-dragging-area": "workspace:*",
"@blocksuite/affine-widget-edgeless-selected-rect": "workspace:*",
"@blocksuite/affine-widget-edgeless-toolbar": "workspace:*",
"@blocksuite/affine-widget-edgeless-zoom-toolbar": "workspace:*",
"@blocksuite/affine-widget-frame-title": "workspace:*",
"@blocksuite/affine-widget-keyboard-toolbar": "workspace:*",
"@blocksuite/affine-widget-linked-doc": "workspace:*",
"@blocksuite/affine-widget-note-slicer": "workspace:*",
"@blocksuite/affine-widget-page-dragging-area": "workspace:*",
"@blocksuite/affine-widget-remote-selection": "workspace:*",
"@blocksuite/affine-widget-scroll-anchoring": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/affine-widget-toolbar": "workspace:*",
"@blocksuite/affine-widget-viewport-overlay": "workspace:*",
"@blocksuite/data-view": "workspace:*",
"@blocksuite/block-std": "workspace:*",
"@blocksuite/blocks": "workspace:*",
"@blocksuite/global": "workspace:*",
"@blocksuite/std": "workspace:*",
"@blocksuite/store": "workspace:*",
"@blocksuite/sync": "workspace:*",
"rxjs": "^7.8.1"
"@blocksuite/inline": "workspace:*",
"@blocksuite/presets": "workspace:*",
"@blocksuite/store": "workspace:*"
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./ext-loader": "./src/ext-loader/index.ts",
"./std": "./src/std/index.ts",
"./std/gfx": "./src/std/gfx.ts",
"./std/inline": "./src/std/inline.ts",
"./std/effects": "./src/std/effects.ts",
"./block-std": "./src/block-std/index.ts",
"./block-std/gfx": "./src/block-std/gfx.ts",
"./global": "./src/global/index.ts",
"./global/utils": "./src/global/utils.ts",
"./global/env": "./src/global/env.ts",
"./global/exceptions": "./src/global/exceptions.ts",
"./global/di": "./src/global/di.ts",
"./global/types": "./src/global/types.ts",
"./global/gfx": "./src/global/gfx.ts",
"./global/disposable": "./src/global/disposable.ts",
"./global/lit": "./src/global/lit.ts",
"./store": "./src/store/index.ts",
"./store/test": "./src/store/test.ts",
"./blocks/attachment": "./src/blocks/attachment/index.ts",
"./blocks/attachment/store": "./src/blocks/attachment/store.ts",
"./blocks/attachment/view": "./src/blocks/attachment/view.ts",
"./blocks/bookmark": "./src/blocks/bookmark/index.ts",
"./blocks/bookmark/store": "./src/blocks/bookmark/store.ts",
"./blocks/bookmark/view": "./src/blocks/bookmark/view.ts",
"./blocks/callout": "./src/blocks/callout/index.ts",
"./blocks/callout/store": "./src/blocks/callout/store.ts",
"./blocks/callout/view": "./src/blocks/callout/view.ts",
"./blocks/code": "./src/blocks/code/index.ts",
"./blocks/code/store": "./src/blocks/code/store.ts",
"./blocks/code/view": "./src/blocks/code/view.ts",
"./blocks/data-view": "./src/blocks/data-view/index.ts",
"./blocks/data-view/store": "./src/blocks/data-view/store.ts",
"./blocks/data-view/view": "./src/blocks/data-view/view.ts",
"./blocks/database": "./src/blocks/database/index.ts",
"./blocks/database/store": "./src/blocks/database/store.ts",
"./blocks/database/view": "./src/blocks/database/view.ts",
"./blocks/divider": "./src/blocks/divider/index.ts",
"./blocks/divider/store": "./src/blocks/divider/store.ts",
"./blocks/divider/view": "./src/blocks/divider/view.ts",
"./blocks/edgeless-text": "./src/blocks/edgeless-text/index.ts",
"./blocks/edgeless-text/store": "./src/blocks/edgeless-text/store.ts",
"./blocks/edgeless-text/view": "./src/blocks/edgeless-text/view.ts",
"./blocks/embed": "./src/blocks/embed/index.ts",
"./blocks/embed/store": "./src/blocks/embed/store.ts",
"./blocks/embed/view": "./src/blocks/embed/view.ts",
"./blocks/embed-doc": "./src/blocks/embed-doc/index.ts",
"./blocks/embed-doc/store": "./src/blocks/embed-doc/store.ts",
"./blocks/embed-doc/view": "./src/blocks/embed-doc/view.ts",
"./blocks/frame": "./src/blocks/frame/index.ts",
"./blocks/frame/store": "./src/blocks/frame/store.ts",
"./blocks/frame/view": "./src/blocks/frame/view.ts",
"./blocks/image": "./src/blocks/image/index.ts",
"./blocks/image/store": "./src/blocks/image/store.ts",
"./blocks/image/view": "./src/blocks/image/view.ts",
"./blocks/latex": "./src/blocks/latex/index.ts",
"./blocks/latex/store": "./src/blocks/latex/store.ts",
"./blocks/latex/view": "./src/blocks/latex/view.ts",
"./blocks/list": "./src/blocks/list/index.ts",
"./blocks/list/store": "./src/blocks/list/store.ts",
"./blocks/list/view": "./src/blocks/list/view.ts",
"./blocks/note": "./src/blocks/note/index.ts",
"./blocks/note/store": "./src/blocks/note/store.ts",
"./blocks/note/view": "./src/blocks/note/view.ts",
"./blocks/paragraph": "./src/blocks/paragraph/index.ts",
"./blocks/paragraph/store": "./src/blocks/paragraph/store.ts",
"./blocks/paragraph/view": "./src/blocks/paragraph/view.ts",
"./blocks/root": "./src/blocks/root/index.ts",
"./blocks/root/store": "./src/blocks/root/store.ts",
"./blocks/root/view": "./src/blocks/root/view.ts",
"./blocks/surface": "./src/blocks/surface/index.ts",
"./blocks/surface/store": "./src/blocks/surface/store.ts",
"./blocks/surface/view": "./src/blocks/surface/view.ts",
"./blocks/surface-ref": "./src/blocks/surface-ref/index.ts",
"./blocks/surface-ref/store": "./src/blocks/surface-ref/store.ts",
"./blocks/surface-ref/view": "./src/blocks/surface-ref/view.ts",
"./blocks/table": "./src/blocks/table/index.ts",
"./blocks/table/store": "./src/blocks/table/store.ts",
"./blocks/table/view": "./src/blocks/table/view.ts",
"./data-view": "./src/data-view/index.ts",
"./data-view/effects": "./src/data-view/effects.ts",
"./inlines/link": "./src/inlines/link/index.ts",
"./inlines/link/store": "./src/inlines/link/store.ts",
"./inlines/link/view": "./src/inlines/link/view.ts",
"./inlines/reference": "./src/inlines/reference/index.ts",
"./inlines/reference/store": "./src/inlines/reference/store.ts",
"./inlines/reference/view": "./src/inlines/reference/view.ts",
"./inlines/preset": "./src/inlines/preset/index.ts",
"./inlines/preset/store": "./src/inlines/preset/store.ts",
"./inlines/preset/view": "./src/inlines/preset/view.ts",
"./inlines/footnote": "./src/inlines/footnote/index.ts",
"./inlines/footnote/view": "./src/inlines/footnote/view.ts",
"./inlines/footnote/store": "./src/inlines/footnote/store.ts",
"./inlines/latex": "./src/inlines/latex/index.ts",
"./inlines/latex/store": "./src/inlines/latex/store.ts",
"./inlines/latex/view": "./src/inlines/latex/view.ts",
"./inlines/mention": "./src/inlines/mention/index.ts",
"./inlines/mention/view": "./src/inlines/mention/view.ts",
"./widgets/drag-handle": "./src/widgets/drag-handle/index.ts",
"./widgets/drag-handle/view": "./src/widgets/drag-handle/view.ts",
"./widgets/edgeless-auto-connect": "./src/widgets/edgeless-auto-connect/index.ts",
"./widgets/edgeless-auto-connect/view": "./src/widgets/edgeless-auto-connect/view.ts",
"./widgets/edgeless-dragging-area": "./src/widgets/edgeless-dragging-area/index.ts",
"./widgets/edgeless-dragging-area/view": "./src/widgets/edgeless-dragging-area/view.ts",
"./widgets/edgeless-toolbar": "./src/widgets/edgeless-toolbar/index.ts",
"./widgets/edgeless-toolbar/view": "./src/widgets/edgeless-toolbar/view.ts",
"./widgets/frame-title": "./src/widgets/frame-title/index.ts",
"./widgets/frame-title/view": "./src/widgets/frame-title/view.ts",
"./widgets/linked-doc": "./src/widgets/linked-doc/index.ts",
"./widgets/linked-doc/view": "./src/widgets/linked-doc/view.ts",
"./widgets/remote-selection": "./src/widgets/remote-selection/index.ts",
"./widgets/remote-selection/view": "./src/widgets/remote-selection/view.ts",
"./widgets/scroll-anchoring": "./src/widgets/scroll-anchoring/index.ts",
"./widgets/scroll-anchoring/view": "./src/widgets/scroll-anchoring/view.ts",
"./widgets/slash-menu": "./src/widgets/slash-menu/index.ts",
"./widgets/slash-menu/view": "./src/widgets/slash-menu/view.ts",
"./widgets/toolbar": "./src/widgets/toolbar/index.ts",
"./widgets/toolbar/view": "./src/widgets/toolbar/view.ts",
"./widgets/keyboard-toolbar": "./src/widgets/keyboard-toolbar/index.ts",
"./widgets/keyboard-toolbar/view": "./src/widgets/keyboard-toolbar/view.ts",
"./widgets/viewport-overlay": "./src/widgets/viewport-overlay/index.ts",
"./widgets/viewport-overlay/view": "./src/widgets/viewport-overlay/view.ts",
"./widgets/page-dragging-area": "./src/widgets/page-dragging-area/index.ts",
"./widgets/page-dragging-area/view": "./src/widgets/page-dragging-area/view.ts",
"./fragments/doc-title": "./src/fragments/doc-title/index.ts",
"./fragments/doc-title/view": "./src/fragments/doc-title/view.ts",
"./fragments/frame-panel": "./src/fragments/frame-panel/index.ts",
"./fragments/frame-panel/view": "./src/fragments/frame-panel/view.ts",
"./fragments/outline": "./src/fragments/outline/index.ts",
"./fragments/outline/view": "./src/fragments/outline/view.ts",
"./fragments/adapter-panel": "./src/fragments/adapter-panel/index.ts",
"./fragments/adapter-panel/view": "./src/fragments/adapter-panel/view.ts",
"./gfx/text": "./src/gfx/text/index.ts",
"./gfx/text/store": "./src/gfx/text/store.ts",
"./gfx/text/view": "./src/gfx/text/view.ts",
"./gfx/brush": "./src/gfx/brush/index.ts",
"./gfx/brush/store": "./src/gfx/brush/store.ts",
"./gfx/brush/view": "./src/gfx/brush/view.ts",
"./gfx/pointer": "./src/gfx/pointer/index.ts",
"./gfx/pointer/view": "./src/gfx/pointer/view.ts",
"./gfx/shape": "./src/gfx/shape/index.ts",
"./gfx/shape/store": "./src/gfx/shape/store.ts",
"./gfx/shape/view": "./src/gfx/shape/view.ts",
"./gfx/link": "./src/gfx/link/index.ts",
"./gfx/link/view": "./src/gfx/link/view.ts",
"./gfx/note": "./src/gfx/note/index.ts",
"./gfx/note/view": "./src/gfx/note/view.ts",
"./gfx/mindmap": "./src/gfx/mindmap/index.ts",
"./gfx/mindmap/store": "./src/gfx/mindmap/store.ts",
"./gfx/mindmap/view": "./src/gfx/mindmap/view.ts",
"./gfx/connector": "./src/gfx/connector/index.ts",
"./gfx/connector/store": "./src/gfx/connector/store.ts",
"./gfx/connector/view": "./src/gfx/connector/view.ts",
"./gfx/group": "./src/gfx/group/index.ts",
"./gfx/group/store": "./src/gfx/group/store.ts",
"./gfx/group/view": "./src/gfx/group/view.ts",
"./gfx/template": "./src/gfx/template/index.ts",
"./gfx/template/view": "./src/gfx/template/view.ts",
"./gfx/turbo-renderer": "./src/gfx/turbo-renderer.ts",
"./components/block-selection": "./src/components/block-selection.ts",
"./components/block-zero-width": "./src/components/block-zero-width.ts",
"./components/caption": "./src/components/caption.ts",
"./components/card-style-dropdown-menu": "./src/components/card-style-dropdown-menu.ts",
"./components/citation": "./src/components/citation.ts",
"./components/color-picker": "./src/components/color-picker.ts",
"./components/context-menu": "./src/components/context-menu.ts",
"./components/date-picker": "./src/components/date-picker.ts",
"./components/drop-indicator": "./src/components/drop-indicator.ts",
"./components/embed-card-modal": "./src/components/embed-card-modal.ts",
"./components/filterable-list": "./src/components/filterable-list.ts",
"./components/highlight-dropdown-menu": "./src/components/highlight-dropdown-menu.ts",
"./components/hover": "./src/components/hover.ts",
"./components/icon-button": "./src/components/icon-button.ts",
"./components/icons": "./src/components/icons.ts",
"./components/link-preview": "./src/components/link-preview.ts",
"./components/linked-doc-title": "./src/components/linked-doc-title.ts",
"./components/notification": "./src/components/notification.ts",
"./components/peek": "./src/components/peek.ts",
"./components/portal": "./src/components/portal.ts",
"./components/smooth-corner": "./src/components/smooth-corner.ts",
"./components/toast": "./src/components/toast.ts",
"./components/toggle-button": "./src/components/toggle-button.ts",
"./components/toggle-switch": "./src/components/toggle-switch.ts",
"./components/toolbar": "./src/components/toolbar.ts",
"./components/view-dropdown-menu": "./src/components/view-dropdown-menu.ts",
"./components/tooltip-content-with-shortcut": "./src/components/tooltip-content-with-shortcut.ts",
"./components/resource": "./src/components/resource.ts",
"./rich-text": "./src/rich-text/index.ts",
"./rich-text/effects": "./src/rich-text/effects.ts",
"./shared/adapters": "./src/shared/adapters.ts",
"./shared/commands": "./src/shared/commands.ts",
"./shared/consts": "./src/shared/consts.ts",
"./shared/selection": "./src/shared/selection.ts",
"./shared/services": "./src/shared/services.ts",
"./shared/styles": "./src/shared/styles.ts",
"./shared/theme": "./src/shared/theme.ts",
"./shared/types": "./src/shared/types.ts",
"./shared/utils": "./src/shared/utils.ts",
"./schemas": "./src/schemas.ts",
"./model": "./src/model/index.ts",
"./sync": "./src/sync/index.ts",
"./extensions/store": "./src/extensions/store.ts",
"./extensions/view": "./src/extensions/view.ts",
"./foundation/store": "./src/foundation/store.ts",
"./foundation/view": "./src/foundation/view.ts"
"./inline": "./src/inline/index.ts",
"./inline/consts": "./src/inline/consts.ts",
"./inline/types": "./src/inline/types.ts",
"./presets": "./src/presets/index.ts",
"./blocks": "./src/blocks/index.ts",
"./blocks/schemas": "./src/blocks/schemas.ts"
},
"typesVersions": {
"*": {
"effects": [
"dist/effects.d.ts"
],
"block-std": [
"dist/block-std/index.d.ts"
],
"block-std/gfx": [
"dist/block-std/gfx.d.ts"
],
"global": [
"dist/global/index.d.ts"
],
"global/utils": [
"dist/global/utils.d.ts"
],
"global/env": [
"dist/global/env.d.ts"
],
"global/exceptions": [
"dist/global/exceptions.d.ts"
],
"global/di": [
"dist/global/di.d.ts"
],
"global/types": [
"dist/global/types.d.ts"
],
"store": [
"dist/store/index.d.ts"
],
"inline": [
"dist/inline/index.d.ts"
],
"inline/consts": [
"dist/inline/consts.d.ts"
],
"inline/types": [
"dist/inline/types.d.ts"
],
"presets": [
"dist/presets/index.d.ts"
],
"blocks": [
"dist/blocks/index.d.ts"
],
"blocks/schemas": [
"dist/blocks/schemas.d.ts"
]
}
},
"files": [
"src",
@@ -292,10 +97,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.21.0",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.8.4",
"vitest": "3.1.3"
}
"version": "0.19.0"
}

View File

@@ -1,170 +0,0 @@
import { DefaultTheme } from '@blocksuite/affine-model';
import { NotionTextAdapter } from '@blocksuite/affine-shared/adapters';
import type { SliceSnapshot } from '@blocksuite/store';
import { describe, expect, test } from 'vitest';
import { createJob } from '../utils/create-job.js';
import { getProvider } from '../utils/get-provider.js';
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
const provider = getProvider();
describe('notion-text to snapshot', () => {
test('basic', () => {
const notionText =
'{"blockType":"text","editing":[["aaa ",[["_"],["b"],["i"]]],["nbbbb ",[["_"],["i"]]],["hjhj ",[["_"]]],["a",[["_"],["c"]]],[" ",[["_"]]],["ccc d",[["_"],["s"]]],["dd",[["_"],["s"]]]]}';
const sliceSnapshot: SliceSnapshot = {
type: 'slice',
content: [
{
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: 'both',
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'aaa ',
attributes: {
underline: true,
bold: true,
italic: true,
},
},
{
insert: 'nbbbb ',
attributes: {
underline: true,
italic: true,
},
},
{
insert: 'hjhj ',
attributes: {
underline: true,
},
},
{
insert: 'a',
attributes: {
underline: true,
code: true,
},
},
{
insert: ' ',
attributes: {
underline: true,
},
},
{
insert: 'ccc d',
attributes: {
underline: true,
strike: true,
},
},
{
insert: 'dd',
attributes: {
underline: true,
strike: true,
},
},
],
},
},
children: [],
},
],
},
],
workspaceId: '',
pageId: '',
};
const ntAdapter = new NotionTextAdapter(createJob(), provider);
const target = ntAdapter.toSliceSnapshot({
file: notionText,
workspaceId: '',
pageId: '',
});
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
});
test('notion text with empty styles array', () => {
const notionText =
'{"blockType":"text","editing":[["a "],["bold text",[["b"]]],[" hello world"]],"selection":{"startIndex":0,"endIndex":23},"action":"copy"}';
const sliceSnapshot: SliceSnapshot = {
type: 'slice',
content: [
{
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: 'both',
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'a ',
},
{
insert: 'bold text',
attributes: {
bold: true,
},
},
{
insert: ' hello world',
},
],
},
},
children: [],
},
],
},
],
workspaceId: '',
pageId: '',
};
const ntAdapter = new NotionTextAdapter(createJob(), provider);
const target = ntAdapter.toSliceSnapshot({
file: notionText,
workspaceId: '',
pageId: '',
});
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
});
});

View File

@@ -1,236 +0,0 @@
import {
addProperty,
copyCellsByProperty,
databaseBlockProperties,
deleteColumn,
getCell,
getProperty,
updateCell,
} from '@blocksuite/affine-block-database';
import {
type CellDataType,
type ColumnDataType,
type DatabaseBlockModel,
DatabaseBlockSchemaExtension,
NoteBlockSchemaExtension,
ParagraphBlockSchemaExtension,
RootBlockSchemaExtension,
} from '@blocksuite/affine-model';
import { propertyModelPresets } from '@blocksuite/data-view/property-pure-presets';
import type { BlockModel, Store } from '@blocksuite/store';
import { Text } from '@blocksuite/store';
import {
createAutoIncrementIdGenerator,
TestWorkspace,
} from '@blocksuite/store/test';
import { beforeEach, describe, expect, test } from 'vitest';
const extensions = [
RootBlockSchemaExtension,
NoteBlockSchemaExtension,
ParagraphBlockSchemaExtension,
DatabaseBlockSchemaExtension,
];
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
return { id: 'test-collection', idGenerator };
}
function createTestDoc(docId = 'doc0') {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc(docId);
doc.load();
return doc.getStore({ extensions });
}
describe('DatabaseManager', () => {
let doc: Store;
let db: DatabaseBlockModel;
let rootId: BlockModel['id'];
let noteBlockId: BlockModel['id'];
let databaseBlockId: BlockModel['id'];
let p1: BlockModel['id'];
let p2: BlockModel['id'];
let col1: ColumnDataType['id'];
let col2: ColumnDataType['id'];
let col3: ColumnDataType['id'];
const selection = [
{ id: '1', value: 'Done', color: 'var(--affine-tag-white)' },
{ id: '2', value: 'TODO', color: 'var(--affine-tag-pink)' },
{ id: '3', value: 'WIP', color: 'var(--affine-tag-blue)' },
];
beforeEach(() => {
doc = createTestDoc();
rootId = doc.addBlock('affine:page', {
title: new Text('database test'),
});
noteBlockId = doc.addBlock('affine:note', {}, rootId);
databaseBlockId = doc.addBlock(
'affine:database',
{
columns: [],
titleColumn: 'Title',
},
noteBlockId
);
const databaseModel = doc.getModelById(
databaseBlockId
) as DatabaseBlockModel;
db = databaseModel;
col1 = addProperty(
db,
'end',
databaseBlockProperties.numberColumnConfig.create('Number')
);
col2 = addProperty(
db,
'end',
propertyModelPresets.selectPropertyModelConfig.create('Single Select', {
options: selection,
})
);
col3 = addProperty(
db,
'end',
databaseBlockProperties.richTextColumnConfig.create('Rich Text')
);
doc.updateBlock(databaseModel, {
columns: [col1, col2, col3],
});
p1 = doc.addBlock(
'affine:paragraph',
{
text: new Text('text1'),
},
databaseBlockId
);
p2 = doc.addBlock(
'affine:paragraph',
{
text: new Text('text2'),
},
databaseBlockId
);
updateCell(db, p1, {
columnId: col1,
value: 0.1,
});
updateCell(db, p2, {
columnId: col2,
value: [selection[1]],
});
});
test('getColumn', () => {
const column = {
...databaseBlockProperties.numberColumnConfig.create('testColumnId'),
id: 'testColumnId',
};
addProperty(db, 'end', column);
const result = getProperty(db, column.id);
expect(result).toEqual(column);
});
test('addColumn', () => {
const column =
databaseBlockProperties.numberColumnConfig.create('Test Column');
const id = addProperty(db, 'end', column);
const result = getProperty(db, id);
expect(result).toMatchObject(column);
expect(result).toHaveProperty('id');
});
test('deleteColumn', () => {
const column = {
...databaseBlockProperties.numberColumnConfig.create('Test Column'),
id: 'testColumnId',
};
addProperty(db, 'end', column);
expect(getProperty(db, column.id)).toEqual(column);
deleteColumn(db, column.id);
expect(getProperty(db, column.id)).toBeUndefined();
});
test('getCell', () => {
const modelId = doc.addBlock(
'affine:paragraph',
{
text: new Text('paragraph'),
},
noteBlockId
);
const column = {
...databaseBlockProperties.numberColumnConfig.create('Test Column'),
id: 'testColumnId',
};
const cell: CellDataType = {
columnId: column.id,
value: 42,
};
addProperty(db, 'end', column);
updateCell(db, modelId, cell);
const model = doc.getModelById(modelId);
expect(model).not.toBeNull();
const result = getCell(db, model!.id, column.id);
expect(result).toEqual(cell);
});
test('updateCell', () => {
const newRowId = doc.addBlock(
'affine:paragraph',
{
text: new Text('text3'),
},
databaseBlockId
);
updateCell(db, newRowId, {
columnId: col2,
value: [selection[2]],
});
const cell = getCell(db, newRowId, col2);
expect(cell).toEqual({
columnId: col2,
value: [selection[2]],
});
});
test('copyCellsByColumn', () => {
const newColId = addProperty(
db,
'end',
propertyModelPresets.selectPropertyModelConfig.create('Copied Select', {
options: selection,
})
);
copyCellsByProperty(db, col2, newColId);
const cell = getCell(db, p2, newColId);
expect(cell).toEqual({
columnId: newColId,
value: [selection[1]],
});
});
});

View File

@@ -1,42 +0,0 @@
import { defaultImageProxyMiddleware } from '@blocksuite/affine-shared/adapters';
import {
Schema,
Transformer,
type TransformerMiddleware,
} from '@blocksuite/store';
import { TestWorkspace } from '@blocksuite/store/test';
import { AffineSchemas } from '../../schemas.js';
import { testStoreExtensions } from './store.js';
declare global {
interface Window {
happyDOM: {
settings: {
fetch: {
disableSameOriginPolicy: boolean;
};
};
};
}
}
export function createJob(middlewares?: TransformerMiddleware[]) {
window.happyDOM.settings.fetch.disableSameOriginPolicy = true;
const testMiddlewares = middlewares ?? [];
testMiddlewares.push(defaultImageProxyMiddleware);
const schema = new Schema().register(AffineSchemas);
const docCollection = new TestWorkspace();
docCollection.storeExtensions = testStoreExtensions;
docCollection.meta.initialize();
return new Transformer({
schema,
blobCRUD: docCollection.blobSync,
middlewares: testMiddlewares,
docCRUD: {
create: (id: string) => docCollection.createDoc(id).getStore({ id }),
get: (id: string) => docCollection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => docCollection.removeDoc(id),
},
});
}

View File

@@ -1,12 +0,0 @@
import { Container } from '@blocksuite/global/di';
import { testStoreExtensions } from './store';
export function getProvider() {
const container = new Container();
const exts = testStoreExtensions;
exts.forEach(ext => {
ext.setup(container);
});
return container.provider();
}

View File

@@ -1,34 +0,0 @@
import type { BlockSnapshot, SliceSnapshot } from '@blocksuite/store';
export function nanoidReplacement(snapshot: BlockSnapshot | SliceSnapshot) {
return JSON.parse(nanoidReplacementString(JSON.stringify(snapshot)));
}
const escapedSnapshotAttributes = new Set([
'"attributes"',
'"conditions"',
'"iconColumn"',
'"background"',
'"LinkedPage"',
'"elementIds"',
'"attachment"',
]);
function nanoidReplacementString(snapshotString: string) {
const re =
/("block:[A-Za-z0-9-_]{10}")|("[A-Za-z0-9-_]{10}")|("[A-Za-z0-9-_]{35}")|("[A-Za-z0-9-_]{10}:[A-Za-z0-9-_]{10}")|("var\(--affine-v2-chip-label-[a-z]{3,10}\)")|("[A-Za-z0-9-_=]{44}")/g;
const matches = snapshotString.matchAll(re);
const matchesReplaceMap = new Map();
let escapedNumber = 0;
Array.from(matches).forEach((match, index) => {
if (escapedSnapshotAttributes.has(match[0])) {
matchesReplaceMap.set(match[0], match[0]);
escapedNumber++;
} else {
matchesReplaceMap.set(
match[0],
`"matchesReplaceMap[${index - escapedNumber}]"`
);
}
});
return snapshotString.replace(re, match => matchesReplaceMap.get(match));
}

View File

@@ -1,7 +0,0 @@
import { StoreExtensionManager } from '@blocksuite/affine-ext-loader';
import { getInternalStoreExtensions } from '../../extensions/store';
const manager = new StoreExtensionManager(getInternalStoreExtensions());
export const testStoreExtensions = manager.get('store');

View File

@@ -0,0 +1 @@
export * from '@blocksuite/block-std/gfx';

View File

@@ -0,0 +1 @@
export * from '@blocksuite/block-std';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-attachment';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-attachment/store';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-attachment/view';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-bookmark';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-block-bookmark/store';

Some files were not shown because too many files have changed in this diff Show More