Giter Site home page Giter Site logo

nocobase / nocobase Goto Github PK

View Code? Open in Web Editor NEW
5.6K 97.0 772.0 136.75 MB

NocoBase is a scalability-first, open-source no-code/low-code platform for building business applications and enterprise solutions.

Home Page: https://www.nocobase.com

License: Other

Shell 0.01% TypeScript 98.54% JavaScript 1.17% Less 0.01% Dockerfile 0.02% CSS 0.01% Smarty 0.05% PEG.js 0.19%
no-code no-code-framework low-code nodejs nocode lowcode internal-tools internal-tool airtable low-code-development

nocobase's Introduction

English | 中文

NocoBase

Note: 📌

NocoBase is in early stage of development and is subject to frequent changes, please use caution in production environments.

Recent major updates

What is NocoBase

NocoBase is a scalability-first, open-source no-code development platform.
Instead of investing years of time and millions of dollars in research and development, deploy NocoBase in a few minutes and you'll have a private, controllable, and extremely scalable no-code development platform!

Homepage:
https://www.nocobase.com/

Online Demo:
https://demo.nocobase.com/new

Documents:
https://docs.nocobase.com/

Contact Us:
[email protected]

Distinctive features

1. Model-driven, separate "user interface" from "data structure"

Most form-, table-, or process-driven no-code products create data structures directly in the user interface, such as Airtable, where adding a new column to a table is adding a new field. This has the advantage of simplicity of use, but the disadvantage of limited functionality and flexibility to meet the needs of more complex scenarios.

NocoBase adopts the design idea of separating the data structure from the user interface, allowing you to create any number of blocks (data views) for the data collections, with different type, styles, content, and actions in each block. This balances the simplicity of no-code operation with the flexibility of native development.

model

2. What you see is what you get

NocoBase enables the development of complex and distinctive business systems, but this does not mean that complex and specialized operations are required. With a single click, configuration options are displayed on the usage interface, and administrators with system configuration privileges can directly configure the user interface in a WYSIWYG manner.

wysiwyg

3. Functions as plugins

NocoBase adopts plugin architecture, all new functions can be realized by developing and installing plugins, and expanding the functions is as easy as installing an APP on your phone.

plugins

Installation

NocoBase supports three installation methods:

  • Installing With Docker (👍Recommended)

    Suitable for no-code scenarios, no code to write. When upgrading, just download the latest image and reboot.

  • Installing from create-nocobase-app CLI

    The business code of the project is completely independent and supports low-code development.

  • Installing from Git source code

    If you want to experience the latest unreleased version, or want to participate in the contribution, you need to make changes and debug on the source code, it is recommended to choose this installation method, which requires a high level of development skills, and if the code has been updated, you can git pull the latest code.

nocobase's People

Contributors

738326776zby avatar altaytahsin avatar anuoua avatar aotemj avatar aydengen avatar baiheinet avatar bierxiensi avatar chareice avatar chenos avatar difly avatar dream2023 avatar dunqing avatar hongboji avatar hujie123456 avatar justwho avatar katherinehhh avatar lainlee avatar lyf-coder avatar mytharcher avatar pearl-cao avatar saxon-y avatar semmywong avatar seonghookim avatar sudongyuer avatar twinkle77 avatar wrightjin avatar xilesun avatar yangpeng4011 avatar zhangzhonghe avatar zhouyanliang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nocobase's Issues

collection编辑field体验提升建议

1)目前使用的是,级联添加,而不是,直接在collection的field table里进行直接编辑。
建议,可以参考quickbase的table的field添加,直接在field table里进行。
2)另外也建议考虑导入excel格式的collection元数据。
总体来说,就是为了提升,编辑效率。
3)增加一个可视化,collection relation的视图,类似于实体关系图。

能否考虑在未来加入流程引擎?

最近偶然发现nocobase,太棒了!非常看好这个项目~
但我目前项目中涉及到流程审批,不止作者及开发团队是否有计划在未来把这个作为插件加入到项目中
非常感谢~

docker方式启动nocobase,连接postgres报错

使用docker方式部署nocobase,并修改.env中的数据库选项,启动时提示缺少pg包

/nocobase/app/node_modules/sequelize/lib/dialects/abstract/connection-manager.js:81
        throw new Error(`Please install ${moduleName} package manually`);
        ^

Error: Please install pg package manually
    at ConnectionManager._loadDialectModule (/nocobase/app/node_modules/sequelize/lib/dialects/abstract/connection-manager.js:81:15)
    at new ConnectionManager (/nocobase/app/node_modules/sequelize/lib/dialects/postgres/connection-manager.js:18:24)
    at new PostgresDialect (/nocobase/app/node_modules/sequelize/lib/dialects/postgres/index.js:15:30)
    at new Sequelize (/nocobase/app/node_modules/sequelize/lib/sequelize.js:340:20)
    at new Database (/nocobase/app/node_modules/@nocobase/database/lib/database.js:152:22)
    at Object.createDatabase (/nocobase/app/node_modules/@nocobase/server/lib/helper.js:76:12)
    at new Application (/nocobase/app/node_modules/@nocobase/server/lib/application.js:84:24)
    at Object.<anonymous> (/nocobase/app/lib/apis/index.js:10:13)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Server 中默认导出只有 `create` 方法,如果导出 `Application` 是否更好?

目前看 create 方法主要是提供了一个使用 server 包的默认范式(resourcer 生成 API,),如果只导出这个方法的话,相当于让使用者只能按我们的预设来使用。

这个方式本身没有问题,但如果我们预期就是这样,那么其实可以把 create 的内容移动到 Application 的构造函数中,并默认导出 Application,这样代码结构会更清晰。

而如果我们希望导出一个空容器,那么 create 方法的内容就应该移动到 create-nocobase-app 的模板中。

或者是否有其他我可能不太了解的考虑?

Extend fields module

Add new fields:
* Auto incrementing number with string type(prefix) with option to set specific length. This must have for orders/invoices and etc...
* Wysiwyg or any other editor.
* Embed field
* Currency field (allow selecting which currencies will be show on select list).
* Group of fields - allow creating group of fields that related with repeatable options.

Add new options to fields:
Allow to make field as required 
Allow to make field as read only
Allow to set default value
Allow to set field as "Personal Data" good for private notes for example
Add tooltip text - this text will be added to icon near the field
Add Auto-increment to field type "number"
Repeatable fields

Add Dynamic Logic
Conditions making field visible
Conditions making field required / read-only and etc...

*** add accessibility to all fields as possible ***

创建相同role name的第二个user会报数据库约束错误

使用版本分支是:nocobase_next
数据库:sqlite3

创建第一个用户正常没有报错:

await User.repository.create({
    values: {
    email: "[email protected]",
    password: "password",
    nickname: "zhangsan",
    roles: ["member"],
    },
});

创建第二个用户:

await User.repository.create({
    values: {
    email: "[email protected]",
    password: "password",
    nickname: "lisi",
    roles: ["member"],
    },
});

将报错:

 err: Error: 
        at Database.<anonymous> (/Users/marshal/projects/demo/node_modules/sequelize/src/dialects/sqlite/query.js:227:27)
        at /Users/marshal/projects/demo/node_modules/sequelize/src/dialects/sqlite/query.js:225:50
        at new Promise (<anonymous>)
        at Query.run (/Users/marshal/projects/demo/node_modules/sequelize/src/dialects/sqlite/query.js:225:12)
        at /Users/marshal/projects/demo/node_modules/sequelize/src/sequelize.js:643:28
        at SQLiteQueryInterface.bulkInsert (/Users/marshal/projects/demo/node_modules/sequelize/src/dialects/abstract/query-interface.js:859:21)
        at recursiveBulkCreate (/Users/marshal/projects/demo/node_modules/sequelize/src/model.js:2776:25)
        at Function.bulkCreate (/Users/marshal/projects/demo/node_modules/sequelize/src/model.js:2902:12)
        at async Promise.all (index 0)
        at BelongsToMany.set (/Users/marshal/projects/demo/node_modules/sequelize/src/associations/belongs-to-many.js:653:14) {
    name: 'SequelizeUniqueConstraintError',
    errors: [ [ValidationErrorItem] ],
    parent: [Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: rolesUsers.roleName] {
        errno: 19,
        code: 'SQLITE_CONSTRAINT',
        sql: "INSERT INTO `rolesUsers` (`createdAt`,`updatedAt`,`userId`,`roleName`) VALUES ('2022-04-04 14:26:38.471 +00:00','2022-04-04 14:26:38.471 +00:00',2,'member');"
    },

查了 rolesUsers 生成的建表语句:

CREATE TABLE `rolesUsers` (`createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `default` TINYINT(1), `userId` INTEGER NOT NULL UNIQUE REFERENCES `users` (`id`), `roleName` VARCHAR(255) NOT NULL UNIQUE REFERENCES `roles` (`name`), PRIMARY KEY (`userId`, `roleName`))

可能是出在 ``roleName VARCHAR(255) NOT NULL UNIQUE,这里 roleName 应该不做唯一约束

另外,``userId INTEGER NOT NULL UNIQUE 是否也不应该唯一约束,否则 user 就只能和 role 多对一关系了

完整的测试代码:

import { Application } from "@nocobase/server";

import PluginACL from "@nocobase/plugin-acl/lib/server";
import UsersPlugin from "@nocobase/plugin-users/lib/server";
import { PluginErrorHandler } from "@nocobase/plugin-error-handler/lib/server";

class MockServer extends Application {}

describe("app test", () => {
  let app: Application;

  beforeAll(async () => {
    app = new MockServer({
      registerActions: true,
      database: {
        dialect: "sqlite",
        storage: ":memory:",
        // storage: "/tmp/tmp.sqlite",
        logging: false,
      },
      resourcer: {
        prefix: `/api`,
      },
    });

    app.plugin(UsersPlugin);
    app.plugin(PluginACL);
    app.plugin(PluginErrorHandler);

    await app.load();
    await app.install({
      sync: {
        force: true,
        alter: {
          drop: true,
        },
      },
    });
  });

  afterAll(async () => {
    await app.destroy();
  });

  it("users test", async () => {
    const User = app.db.getCollection("users");

    await User.repository.create({
      values: {
        email: "[email protected]",
        password: "password",
        nickname: "zhangsan",
        roles: ["admin"],
      },
    });

    await User.repository.create({
      values: {
        email: "[email protected]",
        password: "password",
        nickname: "lisi",
        roles: ["member"],
      },
    });
  });
});

想问下useResource 是在哪里封装的 还是formily自带的 在formily中没有找到这个属性

properties: {
modal: {
type: 'void',
title: "{{t('Create collection')}}",
'x-decorator': 'Form',
'x-decorator-props': {
useResource: useCollectionResource,
},
'x-component': 'Action.Drawer',
'x-component-props': {
useOkAction: '{{ Table.useTableCreateAction }}',
},
// 创建数据表弹窗
properties: {
title: {
type: 'string',
title: "{{t('Collection display name')}}",
'x-component': 'Input',
'x-decorator': 'FormilyFormItem',
},
name: {
type: 'string',
title: "{{t('Collection name')}}",
'x-component': 'Input',
'x-decorator': 'FormilyFormItem',
description:
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
},

Views : Grid, Gallery, Gantt

像airtable一样的视图,目前看板,表单(kanban,form)已经支持了,期待Grid, Gallery, Gantt这三种视图。

在线体验后的几点功能建议

1、目前的表单功能只是普通业务的操作,后续是否会支持工作流?
2、数据表的配置,可能不仅仅是来源于平台数据,有可能是来源于第三方接口服务
3、自定义接口的支持,比如,表单保存前/后需要调用一个接口做一些处理,删除数据前/后,可能需要调用接口做一些逻辑判断,列表数据的来源也可能是一个第三方服务

这可能是目前为止操作最便捷的低代码SaaS平台之一,加油!

Cannot find module '@nocobase/database' or its corresponding type declarations

deploy流程上是不是应该在README中体现更具体一些,我描述下我刚刚的流程:

  1. git clone
  2. npm i
  3. npm run bootstrap 其实也就是lerna bootstrap,安装各模块的依赖
  4. npm run start
➜  nocobase git:(master) ✗ npm run start    

> root@ start /Users/apple/docker/wwwroot/nocobase
> cd packages/app && npm start


> @nocobase/[email protected] start /Users/apple/docker/wwwroot/nocobase/packages/app
> concurrently "nodemon" "umi dev"

[0] [nodemon] 2.0.7
[0] [nodemon] to restart at any time, enter `rs`
[0] [nodemon] watching path(s): ../**/*
[0] [nodemon] watching extensions: ts
[0] [nodemon] starting `ts-node ../api/src/index.ts`
[1] Starting the development server...
[1] ℹ Compiling Webpack
[0] 
[0] /Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:434
[0]     return new TSError(diagnosticText, diagnosticCodes)
[0]            ^
[0] TSError: ⨯ Unable to compile TypeScript:
[0] ../api/src/app.ts(6,33): error TS2307: Cannot find module '@nocobase/database' or its corresponding type declarations.
[0] 
[0]     at createTSError (/Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:434:12)
[0]     at reportTSError (/Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:438:19)
[0]     at getOutput (/Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:578:36)
[0]     at Object.compile (/Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:775:32)
[0]     at Module.m._compile (/Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:858:43)
[0]     at Module._extensions..js (internal/modules/cjs/loader.js:962:10)
[0]     at Object.require.extensions.<computed> [as .ts] (/Users/apple/docker/wwwroot/nocobase/node_modules/ts-node/src/index.ts:861:12)
[0]     at Module.load (internal/modules/cjs/loader.js:798:32)
[0]     at Function.Module._load (internal/modules/cjs/loader.js:711:12)
[0]     at Module.require (internal/modules/cjs/loader.js:838:19)
[0] [nodemon] app crashed - waiting for file changes before starting...
^C[0] nodemon exited with code 130
[1] umi dev exited with code 0

Docker 放在 server 包感觉不太合适

可能现阶段大家对 docker 默认提供镜像的预期还不是特别清晰,我理解未来可能会有三个层次的 usage:

  1. 仅创建一个 Application 实例的空服务:用于特别底层的开发或者特定场合的扩展(非常少);
  2. 通过最小模板(类似当前的 create())创建一个预设了框架核心中间件机制的应用:用于仅创建底层的 API,但仍可以通过各种插件的安装转变成一个生产可用的服务;
  3. 除了使用最小模板,还预置了一些常用插件(用户、权限等),很快速的部署一个基本不用开发的服务并投入使用。

三个层次属于递进的关系,如果都需要覆盖的话,可能可以考虑提供三个镜像。但实际上第一种情况是非常少的,而且已经不需要内置相关中间件机制面向代码的开发者,用不用 docker 其实已经无所谓了,不用更方便。而第二种和第三种情况基本是兼容的,考虑到即使用 docker 创建服务,那么配置(插件)相关的内容也是需要暴露到容器外得以在硬盘上持久化的,所以 2 和 3 应该说都可以,在于我们的权衡。

但如果在默认镜像中要预置常用插件的话,相应的 Dockerfile 就只适合放到 app 或者类似的包里。这里我可能还是倾向于在 server 包里有一个基础镜像,或许可以改名为 nocobase/basenocobase/server 更合适?

Originally posted by @mytharcher in #12 (comment)

@chenos commented:

三个层次的 usage 这点应该是共识的,不过得有 cli 来处理项目的初始化吧?1、2、3 只是 cli 的参数区别。如:

nocobase init # 默认直接创建 APP 项目
nocobase init --onlyAPI # 加了 `--onlyAPI` 参数,可以创建纯 API 服务

需要确定 production 和 development 的 app 目录结构。docker 比 cli 封装度更高,但是应该也是基于 cli 的。

现在 cli 还比较弱,只有个 create-nocobase-app 来初始化 app 项目。

使用MySql数据库的时候出错 npm run db-migrate init 出错

错误内容
process.env.NOCOBASE_ENV
{
database: {
username: 'root',
password: 'root',
database: 'nocobase',
host: 'localhost',
port: '3306',
dialect: 'mysql',
dialectOptions: { charset: 'utf8mb4', collate: 'utf8mb4_unicode_ci' },
pool: { max: 5, min: 0, acquire: 30000, idle: 10000 },
logging: false,
define: {},
sync: { force: true, alter: [Object] }
},
resourcer: { prefix: '/api' }
}
Ignoring invalid configuration option passed to Connection: collate. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection
Ignoring invalid configuration option passed to Connection: collate. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection
(node:9767) UnhandledPromiseRejectionWarning: Error: Invalid value {}
at Object.escape (/var/www/nocobasedemo/node_modules/sequelize/lib/sql-string.js:65:11)
at MySQLQueryGenerator.escape (/var/www/nocobasedemo/node_modules/sequelize/lib/dialects/abstract/query-generator.js:994:22)
at MySQLQueryGenerator.attributeToSQL (/var/www/nocobasedemo/node_modules/sequelize/lib/dialects/mysql/query-generator.js:379:36)
at MySQLQueryGenerator.attributesToSQL (/var/www/nocobasedemo/node_modules/sequelize/lib/dialects/mysql/query-generator.js:434:45)
at MySQLQueryInterface.createTable (/var/www/nocobasedemo/node_modules/sequelize/lib/dialects/abstract/query-interface.js:222:38)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async Function.sync (/var/www/nocobasedemo/node_modules/sequelize/lib/model.js:1300:5)
at async Sequelize.sync (/var/www/nocobasedemo/node_modules/sequelize/lib/sequelize.js:793:35)
(Use node --trace-warnings ... to show where the warning was created)
(node:9767) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:9767) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

npm run bootstrap 在这一步的时候要提高到root操作。
请问目前数据库是否只支持PostgreSQL ,
以及在windows环境下测试,文件中包含":"的文件不能使用,需要修改文件名称

求助:在阿里云上使用RDS Mysql5.7 安装报错,服务器是 CentOS Linux release 8.1.1911 (Core)

[root@iZ236y2lowtZ ~]# cd /www/wwwroot/nocobase
[root@iZ236y2lowtZ nocobase]# ls
node_modules package.json
[root@iZ236y2lowtZ nocobase]# npx nocobase db-init
process.env.NOCOBASE_ENV
{
database: {
username: 'nocobase',
password: 'Noc2ob3ase1',
database: 'nocobase',
host: 'rm-bpxxxxxxxxxxx.mysql.rds.aliyuncs.com',
port: '3306',
dialect: 'mysql',
dialectOptions: { charset: 'utf8mb4' },
pool: { max: 5, min: 0, acquire: 30000, idle: 10000 },
logging: [Function: log],
define: {},
sync: { force: true, alter: [Object] }
},
resourcer: { prefix: '/api' }
}
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Not overriding built-in method from model attribute: changed
Executing (default): DROP TABLE IF EXISTS china_regions;
Executing (default): DROP TABLE IF EXISTS automations_jobs;
Executing (default): DROP TABLE IF EXISTS automations;
Executing (default): DROP TABLE IF EXISTS tabs_permissions;
Executing (default): DROP TABLE IF EXISTS tabs;
Executing (default): DROP TABLE IF EXISTS menus_permissions;
Executing (default): DROP TABLE IF EXISTS users_roles;
Executing (default): DROP TABLE IF EXISTS fields_permissions;
Executing (default): DROP TABLE IF EXISTS actions_permissions;
Executing (default): DROP TABLE IF EXISTS permissions;
Executing (default): DROP TABLE IF EXISTS views_fields_v2;
Executing (default): DROP TABLE IF EXISTS views_details_v2;
Executing (default): DROP TABLE IF EXISTS views_actions_v2;
Executing (default): DROP TABLE IF EXISTS system_settings;
Executing (default): DROP TABLE IF EXISTS attachments;
Executing (default): DROP TABLE IF EXISTS storages;
Executing (default): DROP TABLE IF EXISTS routes_permissions;
Executing (default): DROP TABLE IF EXISTS roles;
Executing (default): DROP TABLE IF EXISTS pages;
Executing (default): DROP TABLE IF EXISTS pages_views_v2;
Executing (default): DROP TABLE IF EXISTS pages_v2;
Executing (default): DROP TABLE IF EXISTS menus_views_v2;
Executing (default): DROP TABLE IF EXISTS views_v2;
Executing (default): DROP TABLE IF EXISTS menus;
Executing (default): DROP TABLE IF EXISTS action_changes;
Executing (default): DROP TABLE IF EXISTS action_logs;
Executing (default): DROP TABLE IF EXISTS users;
Executing (default): DROP TABLE IF EXISTS views;
Executing (default): DROP TABLE IF EXISTS scopes;
Executing (default): DROP TABLE IF EXISTS fields;
Executing (default): DROP TABLE IF EXISTS actions;
Executing (default): DROP TABLE IF EXISTS collections;
Executing (default): DROP TABLE IF EXISTS collections;
(node:676) UnhandledPromiseRejectionWarning: Error: Invalid value {}
at Object.escape (/www/wwwroot/nocobase/node_modules/_sequelize@6.6.5@sequel ize/lib/sql-string.js:65:11)
at MySQLQueryGenerator.escape (/www/wwwroot/nocobase/node_modules/_sequelize @6.6.5@sequelize/lib/dialects/abstract/query-generator.js:994:22)
at MySQLQueryGenerator.attributeToSQL (/www/wwwroot/nocobase/node_modules/s equelize@6.6.5@sequelize/lib/dialects/mysql/query-generator.js:379:36)
at MySQLQueryGenerator.attributesToSQL (/www/wwwroot/nocobase/node_modules/
sequelize@6.6.5@sequelize/lib/dialects/mysql/query-generator.js:434:45)
at MySQLQueryInterface.createTable (/www/wwwroot/nocobase/node_modules/_sequ elize@6.6.5@sequelize/lib/dialects/abstract/query-interface.js:222:38)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Function.sync (/www/wwwroot/nocobase/node_modules/[email protected]@ sequelize/lib/model.js:1300:5)
at async Sequelize.sync (/www/wwwroot/nocobase/node_modules/[email protected] @sequelize/lib/sequelize.js:793:35)
(Use node --trace-warnings ... to show where the warning was created)
(node:676) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This e rror originated either by throwing inside of an async function without a catch b lock, or by rejecting a promise which was not handled with .catch(). To terminat e the node process on unhandled promise rejection, use the CLI flag --unhandled -rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejection s_mode). (rejection id: 1)
(node:676) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprec ated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

PDF support

Hi Guys, great project!
Any plan to add PDF support? (if yes, when you plan this add-on?)
This add-on very useful.
For example, i want to create "invoices" entity. When each invoice created, i want to send the invoice as attached PDF.
Also, i want to be able to view/download the invoice form admin panel.
Of course, PDF add-on should be with some editor to allow design and insert all the fields you want to show inside PDF file.
Then it will be great to save as template and use the template in nocobase where you want.

代码中给member role 增加 resource时能记录数据库,带有异常退出

环境:

  • 代码分支:nocobase-next
  • 使用 sqlite 数据库
  • 使用 repository api

代码想给 role 是 member 的角色用户,设置可以访问 users resource

代码片段:

let result = this.db.getRepository("roles").findById("member") as any;

result = this.db
    .getRepository("roles")
    .relation("resources")
    .of("member");

result = await result.create({
    values: {
    name: "users"
    },
});

报错:

Executing (default): SELECT `createdAt`, `updatedAt`, `name`, `title`, `description`, `strategy`, `default`, `allowConfigure`, `allowNewMenu` FROM `roles` AS `roles` WHERE `roles`.`name` = 'member';
Executing (default): SELECT `createdAt`, `updatedAt`, `name`, `title`, `description`, `strategy`, `default`, `allowConfigure`, `allowNewMenu` FROM `roles` AS `roles` WHERE `roles`.`name` = 'member';
Executing (default): INSERT INTO `rolesResources` (`id`,`createdAt`,`updatedAt`,`roleName`,`name`) VALUES (NULL,$1,$2,$3,$4);
Executing (afb2ca83-5286-4dc3-9978-698850c7ab93): BEGIN DEFERRED TRANSACTION;
Executing (afb2ca83-5286-4dc3-9978-698850c7ab93): COMMIT;
/Users/marshal/projects/demo/node_modules/@nocobase/plugin-acl/lib/model/RoleResourceModel.js:18
            role.revokeResource(resourceName);
                 ^

TypeError: Cannot read properties of undefined (reading 'revokeResource')
    at model.<anonymous> (/Users/marshal/projects/demo/node_modules/@nocobase/plugin-acl/lib/model/RoleResourceModel.js:18:18)

报错的原因是自定义的Model, RoleResourceModel的revoke方法中,传入的 options 没有 role 成员 undefined,是sequelite存储数据库后调用的时候报错的。

是我调用方式有问题,还是nocobase这部分代码需要Eager Loading role对象,还是其他原因。

请帮助,谢谢。

数据表配置中的字段配置问题

在添加字段->系统信息->最后更新日期

这个“最后更新日期”对应的字段实际上是“最后更新人”,应该是页面文字写错了

123

建议是否可以替换 bcrypt 为 bcryptjs

就是目前发现非 mac 平台打包的时候, bcrypt 安装会出现各种编译错误,看项目实际上使用 bcrypt 很简单, 就是一个 hash 方法, 那是否可以直接替换为原生的 js 版本呢?

mysql 数据库 Duplicate column Error

就是发现迁移到mysql(8.0.28) 的时候, 数据的查询操作, 会出现很多重复的 column 错误 ,打印了sqlite 和 mysql 的数据库执行日志, sqlite 确实可以执行, mysql 就会直接报错了。

原始日志

image

格式化后的日志

image

mysql 报错

image

mysql 客户端执行确实报错

image

SELECT
	`users`.*,
	`roles`.`name` AS `roles.name`,
	`roles`.`title` AS `roles.title`,
	`roles`.`created_at` AS `roles.created_at`,
	`roles`.`updated_at` AS `roles.updated_at`,
	`roles->roles_users`.`created_at` AS `roles.roles_users.createdAt`,
	`roles->roles_users`.`updated_at` AS `roles.roles_users.updatedAt`,
	`roles->roles_users`.`user_id` AS `roles.roles_users.user_id`,
	`roles->roles_users`.`role_name` AS `roles.roles_users.role_name` 
FROM
	(
	SELECT
		`users`.`id`,
		`users`.`nickname`,
		`users`.`email`,
		`users`.`password`,
		`users`.`app_lang` AS `appLang`,
		`users`.`token`,
		`users`.`reset_token`,
		`users`.`sort`,
		`users`.`created_at`,
		`users`.`updated_at`,
		`users`.`password`,
		`users`.`email`,
		`users`.`nickname` 
	FROM
		`users` AS `users` 
	ORDER BY
		`users`.`sort` ASC 
		LIMIT 0,
		10 
	) AS `users`
	LEFT OUTER JOIN (
		`roles_users` AS `roles->roles_users`
		INNER JOIN `roles` AS `roles` ON `roles`.`name` = `roles->roles_users`.`role_name` 
	) ON `users`.`id` = `roles->roles_users`.`user_id` 
ORDER BY
	`users`.`sort` ASC

Extend Calendar for full use (not just for tasks)

Calendar should be more dynamic.

Each user need to have manage all his days that include: tasks, calls, meetings etc...

Team manager could see the Calendar of each user in his team and etc...

Calendar should be sync with providers like Outlook, Google and etc...

Click on calendar day/time should open "new event" or edit "exist event".
Now to add new event, you need to click on "add new" button

npm run build failed

$ father-build
[
'database',
'resourcer',
'actions',
'client',
'server',
'api',
'create-nocobase-app',
'plugin-action-logs',
'plugin-automations',
'plugin-china-region',
'plugin-collections',
'plugin-export',
'plugin-file-manager',
'plugin-full-collections',
'plugin-pages',
'plugin-permissions',
'plugin-users'
]
@nocobase/database: Clean dist directory
@nocobase/database: Build cjs with babel
@nocobase/database: Clean lib directory
@nocobase/database: Transform to cjs for src/tests/tables/tests.js
@nocobase/database: Transform to cjs for src/tests/utils/modules/fn.js
@nocobase/database: Transform to cjs for src/tests/utils/modules/obj.js
/Users/mo-fang/nocobase/node_modules/@types/express-serve-static-core/index.d.ts(589,18):error TS2430: Interface 'Response<ResBody, Locals, StatusCode>' incorrectly extends interface 'ServerResponse'.
Property 'req' is optional in type 'Response<ResBody, Locals, StatusCode>' but required in type 'ServerResponse'.
/Users/mo-fang/nocobase/node_modules/@types/express/index.d.ts(58,55): error TS2344: Type 'Response<any, Record<string, any>>' does not satisfy the constraint 'ServerResponse'.
Property 'req' is optional in type 'Response<any, Record<string, any>>' but required in type 'ServerResponse'.
@nocobase/database: Transform to cjs for src/database.js
@nocobase/database: Transform to cjs for src/index.js
@nocobase/database: Transform to cjs for src/model.js
@nocobase/database: Transform to cjs for src/op.js
@nocobase/database: Transform to cjs for src/table.js
@nocobase/database: Transform to cjs for src/utils.js
@nocobase/database: Transform to cjs for src/tests/index.js
@nocobase/database: Transform to cjs for src/fields/field-types.js
@nocobase/database: Transform to cjs for src/fields/index.js
@nocobase/database: Transform to cjs for src/fields/option-types.js
@nocobase/database: Transform to cjs for src/tests/model/getValuesByFieldNames.js
@nocobase/database: Transform to cjs for src/tests/tables/demos.js
@nocobase/database: Transform to cjs for src/tests/tables/extends.js
@nocobase/database: Transform to cjs for src/tests/tables/tests2.js
@nocobase/database: Transform to cjs for src/tests/utils/modules/fnts.js
@nocobase/database: Transform to cjs for src/tests/utils/modules/objts.js
TypeScript: 2 semantic errors
TypeScript: emit succeeded (with errors)
events.js:291
throw er; // Unhandled 'error' event
^

Error: TypeScript: Compilation failed
at Output.mightFinish (/Users/mo-fang/nocobase/node_modules/father-build/node_modules/gulp-typescript/release/output.js:130:43)
at /Users/mo-fang/nocobase/node_modules/father-build/node_modules/gulp-typescript/release/output.js:65:22
Emitted 'error' event on Duplexify instance at:
at Duplexify._destroy (/Users/mo-fang/nocobase/node_modules/duplexify/index.js:191:15)
at /Users/mo-fang/nocobase/node_modules/duplexify/index.js:182:10
at processTicksAndRejections (internal/process/task_queues.js:79:11)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

未登录用户(anonymouse role)如何能使用scope?

使用版本分支是:nocobase_next
数据库:sqlite3

比如我有个资源:

// 文章表
  app.db.collection({
    name: "posts",
    createdBy: true,
    fields: [
      { name: "title", type: "string" },
      { name: "content", type: "text" },
      { name: "published", type: "boolean", defaultValue: false },
    ],
  });

希望未登录用户,即 role 为 anonymous,只能浏览 published==truepost

如果使用 SkipManager,只能跳过 resource 的 action,但不能如 ACL 那样更细粒度的控制访问内容:

app.acl.skip("posts", "get");
app.acl.skip("posts", "list");

我尝试给 anonymous 增加 role 和相关的 rolesActions 和 scope,但并不生效 (报403):

// scope, 已发布的文章
let scope = await Scope.repository.create({
  values: {
    name: "get published posts",
    resourceName: "posts",
    scope: {
      published: true,
    },
  },
});

// anonymouse 角色和权限
let role = await Role.repository.create({
  values: {
    name: "anonymous",
    title: "Anonymous",
    resources: [
      {
        name: "posts",
        actions: [
          {
            name: "list",
            fields: ["title", "content", "published"],
            scope,
          },
          {
            name: "get",
            fields: ["title", "content", "published"],
            scope,
          },
        ],
      },
    ],
  },
});

能否考虑为未登录用户加载 role 相关 acl 数据(如果有的话),这样配置上各种 role 的情况有一致性。

或者有其他方式可以实现类似需求,我可能没有理解。

附上完整的测试代码:

import { Application } from "@nocobase/server";
import request from "supertest";

import PluginACL from "@nocobase/plugin-acl/lib/server";
import UsersPlugin from "@nocobase/plugin-users/lib/server";
import { PluginErrorHandler } from "@nocobase/plugin-error-handler/lib/server";

class MockServer extends Application {}

describe("app test", () => {
  let app: Application;
  beforeAll(async () => {
    app = new MockServer({
      registerActions: true,
      database: {
        dialect: "sqlite",
        storage: ":memory:",
        // storage: "/tmp/tmp.sqlite",
        logging: false,
      },
      resourcer: {
        prefix: `/api`,
      },
    });

    app.plugin(UsersPlugin);
    app.plugin(PluginACL);
    app.plugin(PluginErrorHandler);

    await app.load();
    await app.install({
      sync: {
        force: true,
      },
    });

    // 文章表
    app.db.collection({
      name: "posts",
      createdBy: true,
      fields: [
        { name: "title", type: "string" },
        { name: "content", type: "text" },
        { name: "published", type: "boolean", defaultValue: false },
      ],
    });

    await app.db.sync();

    // app.acl.skip("posts", "get");
    // app.acl.skip("posts", "list");

    // 角色信息同步到 ACL 组件中
    let plugin = app.getPlugin("PluginACL") as PluginACL;
    await plugin.writeRolesToACL();

    // 初始化数据
    const User = app.db.getCollection("users");
    const Role = app.db.getCollection("roles");
    const Scope = app.db.getCollection("rolesResourcesScopes");

    // 创建一个用户
    await User.repository.create({
      values: {
        email: "[email protected]",
        password: "password",
        nickname: "zhangsan",
        roles: ["member"],
      },
    });

    // scope, 已发布的文章
    let scope = await Scope.repository.create({
      values: {
        name: "get published posts",
        resourceName: "posts",
        scope: {
          published: true,
        },
      },
    });

    // anonymouse 角色和权限
    let role = await Role.repository.create({
      values: {
        name: "anonymous",
        title: "Anonymous",
        resources: [
          {
            name: "posts",
            actions: [
              {
                name: "list",
                fields: ["title", "content", "published"],
                scope,
              },
              {
                name: "get",
                fields: ["title", "content", "published"],
                scope,
              },
            ],
          },
        ],
      },
    });

    // member 角色权限
    await Role.repository
      .relation("resources")
      .of("member")
      .create({
        values: {
          name: "posts",
          actions: [
            {
              name: "get",
              fields: ["title", "content", "published"],
              scope,
            },
            {
              name: "list",
              fields: ["title", "content", "published"],
              scope,
            },
            {
              name: "create",
              fields: ["title", "content", "published"],
            },
            {
              name: "update",
              fields: ["title", "content", "published"],
            },
          ],
        },
      });
  });

  afterAll(async () => {
    await app.destroy();
  });

  // 普通用户
  it("using app with member", async () => {
    // 登录
    let response = await request(app.callback())
      .post("/api/users:signin")
      .send({
        email: "[email protected]",
        password: "password",
      });
    expect(response.statusCode).toEqual(200);
    const { token, id: userId } = response.body.data;
    expect(token).toBeDefined();

    // 创建文章且发布;
    response = await request(app.callback())
      .post("/api/posts")
      .set("Authorization", `Bearer ${token}`)
      .send({
        title: "Hello world",
        content: "my first posts.",
        published: true,
      });
    expect(response.statusCode).toEqual(200);

    // 匿名阅读文章列表
    response = await request(app.callback()).get("/api/posts");
    expect(response.statusCode).toEqual(200);
    expect(response.body.data.length).toEqual(1);
  });
});

构建 packages 依赖时报错了, 运行 yarn bootstrap 时报错了

报错信息如下:
:my-nocobase-project apple$ yarn bootstrap
yarn run v1.22.10
warning ../package.json: No license field
$ lerna bootstrap --no-ci
lerna notice cli v3.22.1
lerna info versioning independent
lerna info Bootstrapping 18 packages
lerna info Installing external dependencies
lerna ERR! npm install --no-package-lock exited 1 in '@nocobase/server'
lerna ERR! npm install --no-package-lock stderr:
npm ERR! code 1
npm ERR! path xxx/my-nocobase-project/packages/server/node_modules/libpq
npm ERR! command failed
npm ERR! command sh -c node-gyp rebuild
npm ERR! gyp info it worked if it ends with ok
npm ERR! gyp info using [email protected]
npm ERR! gyp info using [email protected] | darwin | x64
npm ERR! gyp info find Python using Python version 2.7.13 found at "/Users/apple/anaconda2/bin/python"
npm ERR! gyp info spawn /Users/apple/anaconda2/bin/python
npm ERR! gyp info spawn args [
npm ERR! gyp info spawn args 'xxxx/my-nocobase-project/node_modules/node-gyp/gyp/gyp_main.py',
npm ERR! gyp info spawn args 'binding.gyp',
npm ERR! gyp info spawn args '-f',
npm ERR! gyp info spawn args 'make',
npm ERR! gyp info spawn args '-I',
npm ERR! gyp info spawn args 'xxxxx/my-nocobase-project/packages/server/node_modules/libpq/build/config.gypi',
npm ERR! gyp info spawn args '-I',
npm ERR! gyp info spawn args 'xxxxxx/my-nocobase-project/node_modules/node-gyp/addon.gypi',
npm ERR! gyp info spawn args '-I',
npm ERR! gyp info spawn args '/Users/apple/Library/Caches/node-gyp/14.16.0/include/node/common.gypi',
npm ERR! gyp info spawn args '-Dlibrary=shared_library',
npm ERR! gyp info spawn args '-Dvisibility=default',
npm ERR! gyp info spawn args '-Dnode_root_dir=/Users/apple/Library/Caches/node-gyp/14.16.0',
npm ERR! gyp info spawn args '-Dnode_gyp_dir=/xxxx/my-nocobase-project/node_modules/node-gyp',
npm ERR! gyp info spawn args '-Dnode_lib_file=/Users/apple/Library/Caches/node-gyp/14.16.0/<(target_arch)/node.lib',
npm ERR! gyp info spawn args '-Dmodule_root_dir=xxxxx/my-nocobase-project/packages/server/node_modules/libpq',
npm ERR! gyp info spawn args '-Dnode_engine=v8',
npm ERR! gyp info spawn args '--depth=.',
npm ERR! gyp info spawn args '--no-parallel',
npm ERR! gyp info spawn args '--generator-output',
npm ERR! gyp info spawn args 'build',
npm ERR! gyp info spawn args '-Goutput_dir=.'
npm ERR! gyp info spawn args ]
npm ERR! /bin/sh: pg_config: command not found
npm ERR! gyp: Call to 'pg_config --libdir' returned exit status 127 while in binding.gyp. while trying to load binding.gyp
npm ERR! gyp ERR! configure error
npm ERR! gyp ERR! stack Error: gyp failed with exit code: 1
npm ERR! gyp ERR! stack at ChildProcess.onCpExit (/xxxx/my-nocobase-project/node_modules/node-gyp/lib/configure.js:351:16)
npm ERR! gyp ERR! stack at ChildProcess.emit (events.js:315:20)
npm ERR! gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:277:12)
npm ERR! gyp ERR! System Darwin 19.4.0
npm ERR! gyp ERR! command "/usr/local/bin/node" "/xxxxx/my-nocobase-project/node_modules/.bin/node-gyp" "rebuild"
npm ERR! gyp ERR! cwd /xxxxx/my-nocobase-project/packages/server/node_modules/libpq
npm ERR! gyp ERR! node -v v14.16.0
npm ERR! gyp ERR! node-gyp -v v5.1.1
npm ERR! gyp ERR! not ok

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/apple/.npm/_logs/2021-08-19T15_25_55_155Z-debug.log

lerna ERR! npm install --no-package-lock exited 1 in '@nocobase/server'
lerna WARN complete Waiting for 5 child processes to exit. CTRL-C to exit immediately.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

saas版本试用发现2个问题

1)配置字段时,会把上一个字段的标签带到新字段里,如果没有修改就无法修改了;
2)新配置数据表,新增记录保存不正常,不能保存数据,字段包含客户名称,地址(省市县),附件,备注(多行文本),状态(单选下拉框)
3)配置界面刚开始正常,后面到新配置的数据表就进不去了。
4)在订单里增加子表格不成功,增加一个产品明细,包含产品名称(关联产品数据表),数量,价格和备注,价格不能把产品数据表里的价格带入到子表格里
5)列表里字段不能冻结,当字段比较多的时候可以左右拖动,冻结的字段不动
6)没有自动编号类型的字段,例如订单编号不能自动生成
7)列表设置每页多少条数据时无法点击,总点不到数字
8)列表无法设置默认排序规则

npx nocobase db-init

hi
mkdir my-nocobase-project && cd my-nocobase-project
npm init
npm i @nocobase/api @nocobase/app
cp -r node_modules/@nocobase/api/.env.example .env

but when excute command : npx nocobase db-init

image

GraphQL support

Hi,

do you plan to add GraphQL to nocobase? if so, when you plan this.

Thanks

🤔️ Question: Next 版预览

感谢开源。

本地体验了下 v0.5, 挺不错的。然后看了下 next 分支,好像 v0.6 client 代码变动很多。所以想问下,next 分支如何能够在本地预览呢? 表示很想看下 v0.6 的变化 😊

ps: 这个项目有考虑开源共建么?

初始化数据命令出错

node:v16.13.1
npm:8.1.2
yarn run v1.22.10

$ ts-node -r dotenv/config --project tsconfig.apis.json ./src/apis/index.ts init --import-demo --lang=zh-CN
Error: Package subpath './lib/data-types' is not defined by "exports" in C:\Users\ABC\Downloads\nocobase-v0.5.0-alpha.33\my-nocobase-app\node_modules\sequelize\package.json
at new NodeError (node:internal/errors:371:5)
at throwExportsNotFound (node:internal/modules/esm/resolve:440:9)
at packageExportsResolve (node:internal/modules/esm/resolve:692:3)
at resolveExports (node:internal/modules/cjs/loader:482:36)
at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object. (C:\Users\ABC\Downloads\nocobase-v0.5.0-alpha.33\my-nocobase-app\node_modules@nocobase\database\lib\fields\fields\index.ts:84:11)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

有NocoBase开发功能规划放出来吗

目前体验大部分看字段都是随机的,针对数据表API供其他应用使用情况下,如何标识有方案吗?
例如常用的表格导出、刷新等
期待新的功能出来

nocobase/packages/client 文件缺失

在根目录运行:cd packages/app && npm start
发现若干异常:

  • ./style.less in ../client/lib/components/admin-layout/index.js, ../client/lib/schemas/action/index.js and 10 others
  • ./style.less in ../client/lib/schemas/menu/index.js, ../client/lib/schemas/table/index.js
  • ...
    经排查是nocobase/packages/client build时,丢失less文件导致

Internal / External Notifications

Must have add-on in nocobase.

Internal Notifications center can be used to send any type of notifications to user, users or team.
Examples:
Super Admin: can send notifications to all users or group of users with any info.
Admin (client): can send notifications to all users or group that belongs to his organization.
Team manager: can send notifications to all users that belongs to his team only.
New record created (task for example) user/users assigned to this task will get notification.
etc...

*** Internal Notifications center can be styled as "global notifications" without the option to reply and "social activity" which allow user to replay to each other.

External Notifications:
for example, send notification by email, SMS, telegram, WhatsApp etc... to user on new task
new order creates, send notification to client
etc...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.