First pass, can create and update

This commit is contained in:
Tom Moor
2018-11-02 18:50:13 -07:00
parent 4260b5e664
commit 07e61bd347
5 changed files with 88 additions and 25 deletions

View File

@ -25,6 +25,7 @@ class Details extends React.Component<Props> {
form: ?HTMLFormElement;
@observable name: string;
@observable subdomain: string;
@observable avatarUrl: ?string;
componentDidMount() {
@ -51,12 +52,16 @@ class Details extends React.Component<Props> {
this.name = ev.target.value;
};
handleSubdomainChange = (ev: SyntheticInputEvent<*>) => {
this.subdomain = ev.target.value.toLowerCase();
};
handleAvatarUpload = (avatarUrl: string) => {
this.avatarUrl = avatarUrl;
};
handleAvatarError = (error: ?string) => {
this.props.ui.showToast(error || 'Unable to upload new avatar');
this.props.ui.showToast(error || 'Unable to upload new logo');
};
get isValid() {
@ -104,11 +109,28 @@ class Details extends React.Component<Props> {
<form onSubmit={this.handleSubmit} ref={ref => (this.form = ref)}>
<Input
label="Name"
name="name"
value={this.name}
onChange={this.handleNameChange}
required
short
/>
<Input
label="Subdomain"
name="subdomain"
value={this.subdomain}
onChange={this.handleSubdomainChange}
placeholder="Optional"
short
/>
{this.subdomain && (
<HelpText small>
You will be able to access your wiki at{' '}
<strong>{this.subdomain}.getoutline.com</strong>
</HelpText>
)}
<Button type="submit" disabled={isSaving || !this.isValid}>
{isSaving ? 'Saving…' : 'Save'}
</Button>

View File

@ -12,7 +12,7 @@ const { authorize } = policy;
const router = new Router();
router.post('team.update', auth(), async ctx => {
const { name, avatarUrl, sharing } = ctx.body;
const { name, avatarUrl, subdomain, sharing } = ctx.body;
const endpoint = publicS3Endpoint();
const user = ctx.state.user;
@ -20,6 +20,7 @@ router.post('team.update', auth(), async ctx => {
authorize(user, 'update', team);
if (name) team.name = name;
if (subdomain) team.subdomain = subdomain;
if (sharing !== undefined) team.sharing = sharing;
if (avatarUrl && avatarUrl.startsWith(`${endpoint}/uploads/${user.id}`)) {
team.avatarUrl = avatarUrl;

25
server/domains.js Normal file
View File

@ -0,0 +1,25 @@
// @flow
export const RESERVED_SUBDOMAINS = [
'admin',
'api',
'beta',
'blog',
'cdn',
'community',
'developer',
'forum',
'help',
'imap',
'localhost',
'mail',
'ns1',
'ns2',
'ns3',
'ns4',
'smtp',
'support',
'status',
'static',
'test',
'www',
];

View File

@ -0,0 +1,14 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('teams', 'subdomain', {
type: Sequelize.STRING,
allowNull: true,
unique: true
});
await queryInterface.addIndex('teams', ['subdomain']);
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('teams', 'subdomain');
await queryInterface.removeIndex('teams', ['subdomain']);
}
}

View File

@ -2,33 +2,34 @@
import uuid from 'uuid';
import { DataTypes, sequelize, Op } from '../sequelize';
import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3';
import { RESERVED_SUBDOMAINS } from '../domains';
import Collection from './Collection';
import User from './User';
const Team = sequelize.define(
'team',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
name: DataTypes.STRING,
slackId: { type: DataTypes.STRING, allowNull: true },
googleId: { type: DataTypes.STRING, allowNull: true },
avatarUrl: { type: DataTypes.STRING, allowNull: true },
sharing: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
slackData: DataTypes.JSONB,
const Team = sequelize.define('team', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
{
indexes: [
{
unique: true,
fields: ['slackId'],
},
],
}
);
name: DataTypes.STRING,
subdomain: {
type: DataTypes.STRING,
allowNull: true,
validate: {
isLowercase: true,
isAlphanumeric: true,
len: [4, 32],
notIn: [RESERVED_SUBDOMAINS],
},
unique: true,
},
slackId: { type: DataTypes.STRING, allowNull: true },
googleId: { type: DataTypes.STRING, allowNull: true },
avatarUrl: { type: DataTypes.STRING, allowNull: true },
sharing: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
slackData: DataTypes.JSONB,
});
Team.associate = models => {
Team.hasMany(models.Collection, { as: 'collections' });