mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
UBERF-8429: Allow to disable sign-ups (#6934)
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Signed-off-by: Alexey Zinoviev <alexey.zinoviev@xored.com>
This commit is contained in:
parent
2c45db6c41
commit
6b112fc1be
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@ -97,7 +97,8 @@
|
||||
"SES_URL": "",
|
||||
"MINIO_ACCESS_KEY": "minioadmin",
|
||||
"MINIO_SECRET_KEY": "minioadmin",
|
||||
"MINIO_ENDPOINT": "localhost"
|
||||
"MINIO_ENDPOINT": "localhost",
|
||||
// "DISABLE_SIGNUP": "true",
|
||||
// "INIT_SCRIPT_URL": "https://raw.githubusercontent.com/hcengineering/init/main/script.yaml",
|
||||
// "INIT_WORKSPACE": "onboarding",
|
||||
},
|
||||
|
@ -85,6 +85,7 @@ services:
|
||||
- LAST_NAME_FIRST=true
|
||||
- ACCOUNTS_URL=http://host.docker.internal:3000
|
||||
- BRANDING_PATH=/var/cfg/branding.json
|
||||
# - DISABLE_SIGNUP=true
|
||||
# - INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml
|
||||
# - INIT_WORKSPACE=onboarding
|
||||
restart: unless-stopped
|
||||
@ -197,6 +198,7 @@ services:
|
||||
- DESKTOP_UPDATES_URL=https://dist.huly.io
|
||||
- DESKTOP_UPDATES_CHANNEL=dev
|
||||
- BRANDING_URL=http://host.docker.internal:8087/branding.json
|
||||
# - DISABLE_SIGNUP=true
|
||||
restart: unless-stopped
|
||||
transactor:
|
||||
image: hardcoreeng/transactor
|
||||
|
@ -57,6 +57,7 @@
|
||||
export let ignoreInitialValidation: boolean = false
|
||||
export let withProviders: boolean = false
|
||||
export let subtitle: string | undefined = undefined
|
||||
export let signUpDisabled = false
|
||||
|
||||
$: $themeStore.language && validate($themeStore.language)
|
||||
|
||||
@ -138,7 +139,7 @@
|
||||
}}
|
||||
>
|
||||
{#if loginState !== 'none'}
|
||||
<Tabs {loginState} />
|
||||
<Tabs {loginState} {signUpDisabled} />
|
||||
{:else}
|
||||
{#if subtitle !== undefined}
|
||||
<div class="fs-title">
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
export let page: Pages = 'signup'
|
||||
|
||||
const signUpDisabled = getMetadata(login.metadata.DisableSignUp) ?? false
|
||||
let navigateUrl: string | undefined
|
||||
|
||||
onDestroy(location.subscribe(updatePageLoc))
|
||||
@ -134,13 +135,13 @@
|
||||
<Scroller padding={'1rem 0'}>
|
||||
<div class="form-content">
|
||||
{#if page === 'login'}
|
||||
<LoginForm {navigateUrl} />
|
||||
<LoginForm {navigateUrl} {signUpDisabled} />
|
||||
{:else if page === 'signup'}
|
||||
<SignupForm />
|
||||
<SignupForm {signUpDisabled} />
|
||||
{:else if page === 'createWorkspace'}
|
||||
<CreateWorkspaceForm />
|
||||
{:else if page === 'password'}
|
||||
<PasswordRequest />
|
||||
<PasswordRequest {signUpDisabled} />
|
||||
{:else if page === 'recovery'}
|
||||
<PasswordRestore />
|
||||
{:else if page === 'selectWorkspace'}
|
||||
|
@ -21,6 +21,7 @@
|
||||
import login from '../plugin'
|
||||
|
||||
export let navigateUrl: string | undefined = undefined
|
||||
export let signUpDisabled = false
|
||||
|
||||
let method: LoginMethods = LoginMethods.Otp
|
||||
|
||||
@ -44,12 +45,12 @@
|
||||
</script>
|
||||
|
||||
{#if method === LoginMethods.Otp}
|
||||
<LoginOtpForm {navigateUrl} on:change={changeMethod} />
|
||||
<LoginOtpForm {navigateUrl} {signUpDisabled} on:change={changeMethod} />
|
||||
<div class="action">
|
||||
<BottomActionComponent action={loginWithPasswordAction} />
|
||||
</div>
|
||||
{:else}
|
||||
<LoginPasswordForm {navigateUrl} on:change={changeMethod} />
|
||||
<LoginPasswordForm {navigateUrl} {signUpDisabled} on:change={changeMethod} />
|
||||
<div class="action">
|
||||
<BottomActionComponent action={loginWithCodeAction} />
|
||||
</div>
|
||||
|
@ -21,6 +21,7 @@
|
||||
import { OtpLoginSteps, sendOtp } from '../index'
|
||||
|
||||
export let navigateUrl: string | undefined = undefined
|
||||
export let signUpDisabled = false
|
||||
|
||||
const fields = [{ id: 'email', name: 'username', i18n: login.string.Email }]
|
||||
const formData = {
|
||||
@ -57,11 +58,12 @@
|
||||
{fields}
|
||||
object={formData}
|
||||
{action}
|
||||
{signUpDisabled}
|
||||
ignoreInitialValidation
|
||||
withProviders
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if step === OtpLoginSteps.Otp && formData.username !== ''}
|
||||
<OtpForm email={formData.username} {navigateUrl} retryOn={otpRetryOn} on:step={handleStep} />
|
||||
<OtpForm email={formData.username} {signUpDisabled} {navigateUrl} retryOn={otpRetryOn} on:step={handleStep} />
|
||||
{/if}
|
||||
|
@ -21,6 +21,7 @@
|
||||
import login from '../plugin'
|
||||
|
||||
export let navigateUrl: string | undefined = undefined
|
||||
export let signUpDisabled = false
|
||||
|
||||
const fields = [
|
||||
{ id: 'email', name: 'username', i18n: login.string.Email },
|
||||
@ -62,6 +63,7 @@
|
||||
{fields}
|
||||
{object}
|
||||
{action}
|
||||
{signUpDisabled}
|
||||
bottomActions={[recoveryAction]}
|
||||
ignoreInitialValidation
|
||||
withProviders
|
||||
|
@ -28,6 +28,7 @@
|
||||
export let navigateUrl: string | undefined = undefined
|
||||
export let email: string
|
||||
export let retryOn: Timestamp
|
||||
export let signUpDisabled = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -232,7 +233,7 @@
|
||||
style:min-height={$deviceInfo.docHeight > 720 ? '42rem' : '0'}
|
||||
>
|
||||
<div class="header">
|
||||
<Tabs loginState="login" />
|
||||
<Tabs loginState="login" {signUpDisabled} />
|
||||
<div class="description">
|
||||
<Label label={login.string.SentTo} />
|
||||
<span class="email ml-1">
|
||||
|
@ -23,8 +23,9 @@
|
||||
import { BottomAction } from '..'
|
||||
import { signUpAction } from '../actions'
|
||||
|
||||
const fields = [{ id: 'email', name: 'username', i18n: login.string.Email }]
|
||||
export let signUpDisabled = false
|
||||
|
||||
const fields = [{ id: 'email', name: 'username', i18n: login.string.Email }]
|
||||
const object = {
|
||||
username: ''
|
||||
}
|
||||
@ -64,7 +65,7 @@
|
||||
goTo('login')
|
||||
}
|
||||
},
|
||||
signUpAction
|
||||
...(signUpDisabled ? [] : [signUpAction])
|
||||
]
|
||||
</script>
|
||||
|
||||
|
@ -21,6 +21,8 @@
|
||||
import { goTo, signUp } from '../utils'
|
||||
import Form from './Form.svelte'
|
||||
|
||||
export let signUpDisabled = false
|
||||
|
||||
const fields = [
|
||||
{ id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true },
|
||||
{ id: 'family-name', name: 'last', i18n: login.string.LastName, short: true },
|
||||
@ -39,6 +41,10 @@
|
||||
|
||||
let status: Status<any> = OK
|
||||
|
||||
if (signUpDisabled) {
|
||||
goTo('login')
|
||||
}
|
||||
|
||||
const action = {
|
||||
i18n: login.string.SignUp,
|
||||
func: async () => {
|
||||
|
@ -19,6 +19,7 @@
|
||||
import login from '../plugin'
|
||||
|
||||
export let loginState: 'login' | 'signup' | 'none' = 'none'
|
||||
export let signUpDisabled = false
|
||||
|
||||
const goTab = (path: string) => {
|
||||
const loc = getCurrentLocation()
|
||||
@ -29,6 +30,7 @@
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center caption">
|
||||
{#if !signUpDisabled}
|
||||
<a
|
||||
class="title"
|
||||
class:selected={loginState === 'signup'}
|
||||
@ -39,6 +41,7 @@
|
||||
>
|
||||
<Label label={login.string.SignUp} />
|
||||
</a>
|
||||
{/if}
|
||||
<a
|
||||
class="title"
|
||||
class:selected={loginState === 'login'}
|
||||
|
@ -72,7 +72,8 @@ export default plugin(loginId, {
|
||||
LoginTokens: '' as Metadata<Record<string, string>>,
|
||||
LastToken: '' as Metadata<string>,
|
||||
LoginEndpoint: '' as Metadata<string>,
|
||||
LoginEmail: '' as Metadata<string>
|
||||
LoginEmail: '' as Metadata<string>,
|
||||
DisableSignUp: '' as Metadata<boolean>
|
||||
},
|
||||
component: {
|
||||
LoginApp: '' as AnyComponent,
|
||||
|
@ -13,7 +13,8 @@ export function registerGithub (
|
||||
accountsUrl: string,
|
||||
dbPromise: Promise<AccountDB>,
|
||||
frontUrl: string,
|
||||
brandings: BrandingMap
|
||||
brandings: BrandingMap,
|
||||
signUpDisabled?: boolean
|
||||
): string | undefined {
|
||||
const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID
|
||||
const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET
|
||||
@ -69,7 +70,7 @@ export function registerGithub (
|
||||
const [first, last] = ctx.state.user.displayName?.split(' ') ?? [ctx.state.user.username, '']
|
||||
measureCtx.info('Provider auth handler', { email, type: 'github' })
|
||||
if (email !== undefined) {
|
||||
let loginInfo: LoginInfo
|
||||
let loginInfo: LoginInfo | null
|
||||
const state = safeParseAuthState(ctx.query?.state)
|
||||
const branding = getBranding(brandings, state?.branding)
|
||||
const db = await dbPromise
|
||||
@ -78,11 +79,28 @@ export function registerGithub (
|
||||
githubId: ctx.state.user.id
|
||||
})
|
||||
} else {
|
||||
loginInfo = await loginWithProvider(measureCtx, db, null, email, first, last, {
|
||||
loginInfo = await loginWithProvider(
|
||||
measureCtx,
|
||||
db,
|
||||
null,
|
||||
email,
|
||||
first,
|
||||
last,
|
||||
{
|
||||
githubId: ctx.state.user.id
|
||||
})
|
||||
},
|
||||
signUpDisabled
|
||||
)
|
||||
}
|
||||
|
||||
if (loginInfo === null) {
|
||||
measureCtx.info('Failed to auth: no associated account found', {
|
||||
email,
|
||||
type: 'github',
|
||||
user: ctx.state?.user
|
||||
})
|
||||
ctx.redirect(concatLink(branding?.front ?? frontUrl, '/login'))
|
||||
} else {
|
||||
const origin = concatLink(branding?.front ?? frontUrl, '/login/auth')
|
||||
const query = encodeURIComponent(qs.stringify({ token: loginInfo.token }))
|
||||
|
||||
@ -90,6 +108,7 @@ export function registerGithub (
|
||||
// Successful authentication, redirect to your application
|
||||
ctx.redirect(`${origin}?${query}`)
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
measureCtx.error('failed to auth', { err, type: 'github', user: ctx.state?.user })
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ export function registerGoogle (
|
||||
accountsUrl: string,
|
||||
dbPromise: Promise<AccountDB>,
|
||||
frontUrl: string,
|
||||
brandings: BrandingMap
|
||||
brandings: BrandingMap,
|
||||
signUpDisabled?: boolean
|
||||
): string | undefined {
|
||||
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID
|
||||
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET
|
||||
@ -70,22 +71,31 @@ export function registerGoogle (
|
||||
measureCtx.info('Provider auth handler', { email, type: 'google' })
|
||||
if (email !== undefined) {
|
||||
try {
|
||||
let loginInfo: LoginInfo
|
||||
let loginInfo: LoginInfo | null
|
||||
const state = safeParseAuthState(ctx.query?.state)
|
||||
const branding = getBranding(brandings, state?.branding)
|
||||
const db = await dbPromise
|
||||
if (state.inviteId != null && state.inviteId !== '') {
|
||||
loginInfo = await joinWithProvider(measureCtx, db, null, email, first, last, state.inviteId as any)
|
||||
} else {
|
||||
loginInfo = await loginWithProvider(measureCtx, db, null, email, first, last)
|
||||
loginInfo = await loginWithProvider(measureCtx, db, null, email, first, last, undefined, signUpDisabled)
|
||||
}
|
||||
|
||||
if (loginInfo === null) {
|
||||
measureCtx.info('Failed to auth: no associated account found', {
|
||||
email,
|
||||
type: 'google',
|
||||
user: ctx.state?.user
|
||||
})
|
||||
ctx.redirect(concatLink(branding?.front ?? frontUrl, '/login'))
|
||||
} else {
|
||||
const origin = concatLink(branding?.front ?? frontUrl, '/login/auth')
|
||||
const query = encodeURIComponent(qs.stringify({ token: loginInfo.token }))
|
||||
|
||||
// Successful authentication, redirect to your application
|
||||
measureCtx.info('Success auth, redirect', { email, type: 'google', target: origin })
|
||||
ctx.redirect(`${origin}?${query}`)
|
||||
}
|
||||
} catch (err: any) {
|
||||
measureCtx.error('failed to auth', { err, type: 'google', user: ctx.state?.user })
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ export type AuthProvider = (
|
||||
accountsUrl: string,
|
||||
db: Promise<AccountDB>,
|
||||
frontUrl: string,
|
||||
brandings: BrandingMap
|
||||
brandings: BrandingMap,
|
||||
signUpDisabled?: boolean
|
||||
) => string | undefined
|
||||
|
||||
export function registerProviders (
|
||||
@ -28,7 +29,8 @@ export function registerProviders (
|
||||
db: Promise<AccountDB>,
|
||||
serverSecret: string,
|
||||
frontUrl: string | undefined,
|
||||
brandings: BrandingMap
|
||||
brandings: BrandingMap,
|
||||
signUpDisabled: boolean = false
|
||||
): void {
|
||||
const accountsUrl = process.env.ACCOUNTS_URL
|
||||
if (accountsUrl === undefined) {
|
||||
@ -63,7 +65,7 @@ export function registerProviders (
|
||||
const res: string[] = []
|
||||
const providers: AuthProvider[] = [registerGoogle, registerGithub, registerOpenid]
|
||||
for (const provider of providers) {
|
||||
const value = provider(ctx, passport, router, accountsUrl, db, frontUrl, brandings)
|
||||
const value = provider(ctx, passport, router, accountsUrl, db, frontUrl, brandings, signUpDisabled)
|
||||
if (value !== undefined) res.push(value)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,8 @@ export function registerOpenid (
|
||||
accountsUrl: string,
|
||||
dbPromise: Promise<AccountDB>,
|
||||
frontUrl: string,
|
||||
brandings: BrandingMap
|
||||
brandings: BrandingMap,
|
||||
signUpDisabled?: boolean
|
||||
): string | undefined {
|
||||
const openidClientId = process.env.OPENID_CLIENT_ID
|
||||
const openidClientSecret = process.env.OPENID_CLIENT_SECRET
|
||||
@ -90,7 +91,7 @@ export function registerOpenid (
|
||||
const [first, last] = ctx.state.user.name?.split(' ') ?? [ctx.state.user.username, '']
|
||||
measureCtx.info('Provider auth handler', { email, type: 'openid' })
|
||||
if (email !== undefined) {
|
||||
let loginInfo: LoginInfo
|
||||
let loginInfo: LoginInfo | null
|
||||
const state = safeParseAuthState(ctx.query?.state)
|
||||
const branding = getBranding(brandings, state?.branding)
|
||||
const db = await dbPromise
|
||||
@ -99,11 +100,28 @@ export function registerOpenid (
|
||||
openId: ctx.state.user.sub
|
||||
})
|
||||
} else {
|
||||
loginInfo = await loginWithProvider(measureCtx, db, null, email, first, last, {
|
||||
loginInfo = await loginWithProvider(
|
||||
measureCtx,
|
||||
db,
|
||||
null,
|
||||
email,
|
||||
first,
|
||||
last,
|
||||
{
|
||||
openId: ctx.state.user.sub
|
||||
})
|
||||
},
|
||||
signUpDisabled
|
||||
)
|
||||
}
|
||||
|
||||
if (loginInfo === null) {
|
||||
measureCtx.info('Failed to auth: no associated account found', {
|
||||
email,
|
||||
type: 'openid',
|
||||
user: ctx.state?.user
|
||||
})
|
||||
ctx.redirect(concatLink(branding?.front ?? frontUrl, '/login'))
|
||||
} else {
|
||||
const origin = concatLink(branding?.front ?? frontUrl, '/login/auth')
|
||||
const query = encodeURIComponent(qs.stringify({ token: loginInfo.token }))
|
||||
|
||||
@ -111,6 +129,7 @@ export function registerOpenid (
|
||||
// Successful authentication, redirect to your application
|
||||
ctx.redirect(`${origin}?${query}`)
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
measureCtx.error('failed to auth', { err, type: 'openid', user: ctx.state?.user })
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ export function registerToken (
|
||||
accountsUrl: string,
|
||||
dbPromise: Promise<AccountDB>,
|
||||
frontUrl: string,
|
||||
brandings: BrandingMap
|
||||
brandings: BrandingMap,
|
||||
signUpDisabled?: boolean
|
||||
): string | undefined {
|
||||
passport.use(
|
||||
'token',
|
||||
|
@ -30,7 +30,6 @@ import os from 'os'
|
||||
*/
|
||||
export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap, onClose?: () => void): void {
|
||||
console.log('Starting account service with brandings: ', brandings)
|
||||
const methods = getMethods()
|
||||
const ACCOUNT_PORT = parseInt(process.env.ACCOUNT_PORT ?? '3000')
|
||||
const dbUrl = process.env.DB_URL
|
||||
if (dbUrl === undefined) {
|
||||
@ -81,6 +80,9 @@ export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap
|
||||
setMetadata(toolPlugin.metadata.InitScriptURL, initScriptUrl)
|
||||
}
|
||||
|
||||
const hasSignUp = process.env.DISABLE_SIGNUP !== 'true'
|
||||
const methods = getMethods(hasSignUp)
|
||||
|
||||
const accountsDb = getAccountDB(dbUrl)
|
||||
|
||||
const app = new Koa()
|
||||
@ -105,7 +107,8 @@ export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap
|
||||
}),
|
||||
serverSecret,
|
||||
frontURL,
|
||||
brandings
|
||||
brandings,
|
||||
!hasSignUp
|
||||
)
|
||||
|
||||
void accountsDb.then((res) => {
|
||||
|
@ -2334,8 +2334,9 @@ export async function loginWithProvider (
|
||||
_email: string,
|
||||
first: string,
|
||||
last: string,
|
||||
extra?: Record<string, string>
|
||||
): Promise<LoginInfo> {
|
||||
extra?: Record<string, string>,
|
||||
signUpDisabled: boolean = false
|
||||
): Promise<LoginInfo | null> {
|
||||
try {
|
||||
const email = cleanEmail(_email)
|
||||
if (last == null) {
|
||||
@ -2357,6 +2358,11 @@ export async function loginWithProvider (
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
if (signUpDisabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
const newAccount = await createAcc(ctx, db, branding, email, null, first, last, true, true, extra)
|
||||
|
||||
const result = {
|
||||
@ -2463,7 +2469,7 @@ export async function deleteWorkspace (
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function getMethods (): Record<string, AccountMethod> {
|
||||
export function getMethods (hasSignUp: boolean = true): Record<string, AccountMethod> {
|
||||
return {
|
||||
login: wrap(login),
|
||||
join: wrap(join),
|
||||
@ -2478,7 +2484,7 @@ export function getMethods (): Record<string, AccountMethod> {
|
||||
getInviteLink: wrap(getInviteLink),
|
||||
getAccountInfo: wrap(getAccountInfo),
|
||||
getWorkspaceInfo: wrap(getWorkspaceInfo),
|
||||
createAccount: wrap(createAccount),
|
||||
...(hasSignUp ? { createAccount: wrap(createAccount) } : {}),
|
||||
createWorkspace: wrap(createUserWorkspace),
|
||||
assignWorkspace: wrap(assignWorkspace),
|
||||
removeWorkspace: wrap(removeWorkspace),
|
||||
|
@ -257,6 +257,7 @@ export function start (
|
||||
brandingUrl?: string
|
||||
previewConfig: string
|
||||
pushPublicKey?: string
|
||||
disableSignUp?: string
|
||||
},
|
||||
port: number,
|
||||
extraConfig?: Record<string, string | undefined>
|
||||
@ -308,6 +309,7 @@ export function start (
|
||||
BRANDING_URL: config.brandingUrl,
|
||||
PREVIEW_CONFIG: config.previewConfig,
|
||||
PUSH_PUBLIC_KEY: config.pushPublicKey,
|
||||
DISABLE_SIGNUP: config.disableSignUp,
|
||||
...(extraConfig ?? {})
|
||||
}
|
||||
res.status(200)
|
||||
|
@ -118,6 +118,8 @@ export function startFront (ctx: MeasureContext, extraConfig?: Record<string, st
|
||||
|
||||
setMetadata(serverToken.metadata.Secret, serverSecret)
|
||||
|
||||
const disableSignUp = process.env.DISABLE_SIGNUP
|
||||
|
||||
const config = {
|
||||
elasticUrl,
|
||||
storageAdapter,
|
||||
@ -134,7 +136,8 @@ export function startFront (ctx: MeasureContext, extraConfig?: Record<string, st
|
||||
collaborator,
|
||||
brandingUrl,
|
||||
previewConfig,
|
||||
pushPublicKey
|
||||
pushPublicKey,
|
||||
disableSignUp
|
||||
}
|
||||
console.log('Starting Front service with', config)
|
||||
const shutdown = start(ctx, config, SERVER_PORT, extraConfig)
|
||||
|
Loading…
Reference in New Issue
Block a user