mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
FSL-1.0-Apache-2.0 grants us the code, not the name. "Sentry" is Functional Software's trademark, so every place the running app says it to a user -- page titles, onboarding, empty states, toasts, settings copy, the self-hosted footer -- now says Sentinel. Attribution is the other half of the deal and stays untouched: upstream copyright headers, LICENSE, NOTICE. So does anything where the mark is a fact rather than a brand -- @sentry/* packages, sentry-cli, the wizard and bundler plugins, docs.sentry.io, the whole gettingStartedDocs tree. Renaming those would leave instructions that no longer work. Nothing structural moved. No import, identifier, route path, feature flag, API field or test id changed; the diff is 312 lines in and 312 out. Colocated specs were updated in lockstep with the copy they assert. Marketing and community links point at hanzo.ai, only where a real page exists to point at. Co-authored-by: Hanzo Dev <[email protected]>
183 lines
5.2 KiB
TypeScript
183 lines
5.2 KiB
TypeScript
import {createFilter} from 'react-select';
|
|
import styled from '@emotion/styled';
|
|
import {PlatformIcon} from 'platformicons';
|
|
|
|
import type {Field} from 'sentry/components/forms/types';
|
|
import platforms from 'sentry/data/platforms';
|
|
import {t, tct, tn} from 'sentry/locale';
|
|
import {space} from 'sentry/styles/space';
|
|
import {convertMultilineFieldValue, extractMultilineFields} from 'sentry/utils';
|
|
import getDynamicText from 'sentry/utils/getDynamicText';
|
|
import slugify from 'sentry/utils/slugify';
|
|
|
|
// Export route to make these forms searchable by label/help
|
|
export const route = '/settings/:orgId/projects/:projectId/';
|
|
|
|
const getResolveAgeAllowedValues = () => {
|
|
let i = 0;
|
|
const values: number[] = [];
|
|
while (i <= 720) {
|
|
values.push(i);
|
|
if (i < 12) {
|
|
i += 1;
|
|
} else if (i < 24) {
|
|
i += 3;
|
|
} else if (i < 36) {
|
|
i += 6;
|
|
} else if (i < 48) {
|
|
i += 12;
|
|
} else {
|
|
i += 24;
|
|
}
|
|
}
|
|
return values;
|
|
};
|
|
|
|
const RESOLVE_AGE_ALLOWED_VALUES = getResolveAgeAllowedValues();
|
|
|
|
const ORG_DISABLED_REASON = t(
|
|
"This option is enforced by your organization's settings and cannot be customized per-project."
|
|
);
|
|
|
|
const PlatformWrapper = styled('div')`
|
|
display: flex;
|
|
align-items: center;
|
|
`;
|
|
const StyledPlatformIcon = styled(PlatformIcon)`
|
|
margin-right: ${space(1)};
|
|
`;
|
|
|
|
export const fields: Record<string, Field> = {
|
|
name: {
|
|
name: 'name',
|
|
type: 'string',
|
|
required: true,
|
|
label: t('Name'),
|
|
placeholder: t('my-awesome-project'),
|
|
help: t('A name for this project'),
|
|
transformInput: slugify,
|
|
getData: (data: {name?: string}) => {
|
|
return {
|
|
name: data.name,
|
|
slug: data.name,
|
|
};
|
|
},
|
|
|
|
saveOnBlur: false,
|
|
saveMessageAlertType: 'info',
|
|
saveMessage: t('You will be redirected to the new project slug after saving'),
|
|
},
|
|
|
|
platform: {
|
|
name: 'platform',
|
|
type: 'select',
|
|
label: t('Platform'),
|
|
options: platforms.map(({id, name}) => ({
|
|
value: id,
|
|
label: (
|
|
<PlatformWrapper key={id}>
|
|
<StyledPlatformIcon platform={id} />
|
|
{name}
|
|
</PlatformWrapper>
|
|
),
|
|
})),
|
|
help: t('The primary platform for this project'),
|
|
filterOption: createFilter({
|
|
stringify: option => {
|
|
const matchedPlatform = platforms.find(({id}) => id === option.value);
|
|
return `${matchedPlatform?.name} ${option.value}`;
|
|
},
|
|
}),
|
|
},
|
|
|
|
subjectPrefix: {
|
|
name: 'subjectPrefix',
|
|
type: 'string',
|
|
label: t('Subject Prefix'),
|
|
placeholder: t('e.g. [my-org]'),
|
|
help: t('Choose a custom prefix for emails from this project'),
|
|
},
|
|
|
|
resolveAge: {
|
|
name: 'resolveAge',
|
|
type: 'range',
|
|
allowedValues: RESOLVE_AGE_ALLOWED_VALUES,
|
|
label: t('Auto Resolve'),
|
|
help: t(
|
|
"Automatically resolve an issue if it hasn't been seen for this amount of time"
|
|
),
|
|
formatLabel: val => {
|
|
val = Number(val);
|
|
if (val === 0) {
|
|
return t('Disabled');
|
|
}
|
|
|
|
if (val > 23 && val % 24 === 0) {
|
|
// Based on allowed values, val % 24 should always be true
|
|
val = val / 24;
|
|
return tn('%s day', '%s days', val);
|
|
}
|
|
|
|
return tn('%s hour', '%s hours', val);
|
|
},
|
|
saveOnBlur: false,
|
|
saveMessage: tct(
|
|
'[strong:Caution]: Enabling auto resolve will immediately resolve anything that has not been seen within this period of time. There is no undo!',
|
|
{
|
|
strong: <strong />,
|
|
}
|
|
),
|
|
saveMessageAlertType: 'warning',
|
|
},
|
|
allowedDomains: {
|
|
name: 'allowedDomains',
|
|
type: 'string',
|
|
multiline: true,
|
|
autosize: true,
|
|
maxRows: 10,
|
|
rows: 1,
|
|
placeholder: t('https://example.com or example.com'),
|
|
label: t('Allowed Domains'),
|
|
help: t(
|
|
'Examples: https://example.com, *, *.example.com, *:80. Separate multiple entries with a newline'
|
|
),
|
|
getValue: val => extractMultilineFields(val),
|
|
setValue: val => convertMultilineFieldValue(val),
|
|
},
|
|
scrapeJavaScript: {
|
|
name: 'scrapeJavaScript',
|
|
type: 'boolean',
|
|
// if this is off for the organization, it cannot be enabled for the project
|
|
disabled: ({organization, name}) => !organization[name],
|
|
disabledReason: ORG_DISABLED_REASON,
|
|
// `props` are the props given to FormField
|
|
setValue: (val, props) => props.organization?.[props.name] && val,
|
|
label: t('Enable JavaScript source fetching'),
|
|
help: t('Allow Sentinel to scrape missing JavaScript source context when possible'),
|
|
},
|
|
securityToken: {
|
|
name: 'securityToken',
|
|
type: 'string',
|
|
label: t('Security Token'),
|
|
help: t(
|
|
'Outbound requests matching Allowed Domains will have the header "{token_header}: {token}" appended'
|
|
),
|
|
setValue: value => getDynamicText({value, fixed: '__SECURITY_TOKEN__'}),
|
|
},
|
|
securityTokenHeader: {
|
|
name: 'securityTokenHeader',
|
|
type: 'string',
|
|
placeholder: t('X-Sentry-Token'),
|
|
label: t('Security Token Header'),
|
|
help: t(
|
|
'Outbound requests matching Allowed Domains will have the header "{token_header}: {token}" appended'
|
|
),
|
|
},
|
|
verifySSL: {
|
|
name: 'verifySSL',
|
|
type: 'boolean',
|
|
label: t('Verify TLS/SSL'),
|
|
help: t('Outbound requests will verify TLS (sometimes known as SSL) connections'),
|
|
},
|
|
};
|