Skip to content

Commit 98503f4

Browse files
committed
Add missing internal actions and a command to run them
1 parent 6e94996 commit 98503f4

26 files changed

+1659
-22
lines changed

src/entry.ts

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -177,34 +177,50 @@ program
177177

178178
const [cmd, ...args] = program.parse().args
179179
async function main() {
180-
if (cmd === 'start') {
181-
const envVars = (await import('./env')).default
182-
// start the internal web socket server
183-
import('./wss/index')
180+
switch (cmd) {
181+
case 'start': {
182+
const envVars = (await import('./env')).default
183+
// start the internal web socket server
184+
import('./wss/index')
184185

185-
const app = express()
186+
const app = express()
186187

187-
const mainAppServer = (await import('./server/index')).default
188+
const mainAppServer = (await import('./server/index')).default
188189

189-
app.use(mainAppServer)
190+
app.use(mainAppServer)
190191

191-
const server = http.createServer(app)
192+
const server = http.createServer(app)
192193

193-
const wss = new WebSocketServer({ server, path: '/websocket' })
194-
const { setupWebSocketServer } = await import('./wss/wss')
195-
setupWebSocketServer(wss)
194+
const wss = new WebSocketServer({ server, path: '/websocket' })
195+
const { setupWebSocketServer } = await import('./wss/wss')
196+
setupWebSocketServer(wss)
196197

197-
server.listen(Number(envVars.PORT), () => {
198-
logger.info(
199-
`📡 Interval Server listening at http://localhost:${envVars.PORT}`
200-
)
201-
})
202-
} else if (cmd === 'db-init') {
203-
logger.info('Initializing a database...')
204-
initDb({ skipCreate: args.includes('--skip-create') }).catch(() => {
205-
logger.error(`Failed to initialize database.`)
206-
process.exit(1)
207-
})
198+
server.listen(Number(envVars.PORT), () => {
199+
logger.info(
200+
`📡 Interval Server listening at http://localhost:${envVars.PORT}`
201+
)
202+
203+
if (args.includes('--internal-actions')) {
204+
import('./interval').catch(err => {
205+
logger.error('Failed starting internal actions:', err)
206+
})
207+
}
208+
})
209+
210+
break
211+
}
212+
case 'internal': {
213+
await import('./interval')
214+
break
215+
}
216+
case 'db-init': {
217+
logger.info('Initializing a database...')
218+
initDb({ skipCreate: args.includes('--skip-create') }).catch(() => {
219+
logger.error('Failed to initialize database.')
220+
process.exit(1)
221+
})
222+
break
223+
}
208224
}
209225
}
210226

src/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ const schema = z.object({
1616
WSS_API_SECRET: z.string(),
1717
AUTH_COOKIE_SECRET: z.string(),
1818

19+
// for internal interval host
20+
INTERVAL_KEY: z.string().optional(),
21+
1922
GIT_COMMIT: z.string().optional(),
2023
PORT: z.string().optional().default('3000'),
2124

src/interval/helpers/findUsers.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import prisma from '~/server/prisma'
2+
export default async function findUsers(query: string) {
3+
return prisma.user.findMany({
4+
where: {
5+
OR: [
6+
{
7+
firstName: {
8+
mode: 'insensitive',
9+
contains: query,
10+
},
11+
},
12+
{
13+
lastName: {
14+
mode: 'insensitive',
15+
contains: query,
16+
},
17+
},
18+
],
19+
},
20+
})
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { MenuItem } from '@interval/sdk/dist/types'
2+
import { Organization, OrganizationSSO } from '@prisma/client'
3+
4+
export default function orgRowMenuItems(
5+
row: Organization & { sso?: Pick<OrganizationSSO, 'id'> | null }
6+
): MenuItem[] {
7+
return [
8+
{
9+
label: 'Browse app structure',
10+
action: 'organizations/app_structure',
11+
params: { org: row.slug },
12+
},
13+
{
14+
label: 'Change slug',
15+
action: 'organizations/change_slug',
16+
params: { org: row.slug },
17+
},
18+
{
19+
label: 'Enable SSO',
20+
action: 'organizations/create_org_sso',
21+
params: { org: row.slug },
22+
},
23+
{
24+
label: 'Disable SSO',
25+
action: 'organizations/disable_org_sso',
26+
params: { org: row.slug },
27+
disabled: row.sso === null,
28+
},
29+
{
30+
label: 'Toggle feature flag',
31+
action: 'organizations/org_feature_flag',
32+
params: { org: row.slug },
33+
},
34+
{
35+
label: 'Transfer owner',
36+
action: 'organizations/transfer_ownership',
37+
params: { org: row.slug },
38+
},
39+
{
40+
label: 'Create transaction history export',
41+
action: 'organizations/create_transaction_history_export',
42+
params: { org: row.slug },
43+
},
44+
]
45+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { User } from '@prisma/client'
2+
3+
export default function renderUserResult(user: User) {
4+
return {
5+
label: [user.firstName, user.lastName].join(' '),
6+
description: user.email,
7+
}
8+
}

src/interval/helpers/requireOrg.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ctx } from '@interval/sdk'
2+
import prisma from '~/server/prisma'
3+
import selectOrganization from './selectOrganization'
4+
5+
export default async function requireOrg(paramName = 'org') {
6+
if (paramName in ctx.params) {
7+
const org = await prisma.organization.findFirst({
8+
where: { slug: String(ctx.params[paramName]) },
9+
include: {
10+
sso: true,
11+
environments: true,
12+
},
13+
})
14+
15+
if (!org) throw new Error('Org not found')
16+
17+
return org
18+
}
19+
20+
return selectOrganization()
21+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ctx } from '@interval/sdk'
2+
3+
export default function requireParam(key: string) {
4+
if (ctx.params[key] === undefined) {
5+
throw new Error(`Missing required param: ${key}`)
6+
}
7+
8+
return String(ctx.params[key])
9+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { io } from '@interval/sdk'
2+
import { Prisma } from '@prisma/client'
3+
4+
import prisma from '~/server/prisma'
5+
6+
export default async function searchForOrganization() {
7+
return io.search<Prisma.OrganizationGetPayload<{ include: { owner: true } }>>(
8+
'Select organization',
9+
{
10+
async onSearch(query) {
11+
return prisma.organization.findMany({
12+
where: {
13+
OR: [
14+
{
15+
name: {
16+
search: query,
17+
mode: 'insensitive',
18+
},
19+
},
20+
{
21+
name: {
22+
contains: query,
23+
mode: 'insensitive',
24+
},
25+
},
26+
],
27+
},
28+
include: {
29+
owner: true,
30+
},
31+
})
32+
},
33+
renderResult: org => ({
34+
value: org.id,
35+
label: org.name,
36+
description: `Owner: ${org.owner.firstName} ${org.owner.lastName} (${org.owner.email}), Slug: ${org.slug}`,
37+
}),
38+
}
39+
)
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import prisma from '~/server/prisma'
2+
import searchForOrganization from './searchForOrganization'
3+
4+
export default async function selectOrganization() {
5+
const selected = await searchForOrganization()
6+
7+
const org = await prisma.organization.findUnique({
8+
where: {
9+
id: selected.id,
10+
},
11+
include: {
12+
sso: true,
13+
environments: true,
14+
},
15+
})
16+
17+
if (!org) {
18+
throw new Error("Organization doesn't exist?")
19+
}
20+
21+
return org
22+
}

src/interval/helpers/selectUser.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { io } from '@interval/sdk'
2+
import findUsers from './findUsers'
3+
import renderUserResult from './renderUserResult'
4+
5+
export default function selectUser() {
6+
return io.search('Select a user', {
7+
onSearch: async q => await findUsers(q),
8+
renderResult: renderUserResult,
9+
})
10+
}

0 commit comments

Comments
 (0)