Skip to content

Commit fb1c6d3

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

27 files changed

+1671
-25
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@interval/server",
33
"description": "Interval Server is the central server for Interval apps",
4-
"version": "1.0.1",
4+
"version": "1.1.0-dev.0+internal",
55
"license": "MIT",
66
"engines": {
77
"node": ">=16"
@@ -22,7 +22,7 @@
2222
"build": "rm -rf dist && prisma generate && yarn server:build && yarn wss:build && yarn client:build",
2323
"pkg": "./scripts/pkg.sh",
2424
"pub": "./scripts/pub.sh",
25-
"start": "NODE_ENV=production ./dist/src/entry.js",
25+
"start": "NODE_ENV=production node ./dist/src/entry.js",
2626
"docker": "./scripts/docker-build.sh",
2727
"client:build": "vite build",
2828
"client:clean": "rm -r node_modules/.vite",

src/entry.ts

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,16 @@ program
165165
.name('interval-server')
166166
.description('Interval Server is the central server for Interval apps')
167167
.option('-v, --verbose', 'verbose output')
168-
.addCommand(new Command('start').description('starts Interval Server'))
168+
.addCommand(
169+
new Command('start')
170+
.description('starts Interval Server')
171+
.addOption(
172+
new Option(
173+
'--internal-actions',
174+
'start interval internal actions along with the server'
175+
)
176+
)
177+
)
169178
.addCommand(
170179
new Command('db-init').addOption(
171180
new Option(
@@ -177,34 +186,50 @@ program
177186

178187
const [cmd, ...args] = program.parse().args
179188
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')
189+
switch (cmd) {
190+
case 'start': {
191+
const envVars = (await import('./env')).default
192+
// start the internal web socket server
193+
import('./wss/index')
184194

185-
const app = express()
195+
const app = express()
186196

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

189-
app.use(mainAppServer)
199+
app.use(mainAppServer)
190200

191-
const server = http.createServer(app)
201+
const server = http.createServer(app)
192202

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

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-
})
207+
server.listen(Number(envVars.PORT), () => {
208+
logger.info(
209+
`📡 Interval Server listening at http://localhost:${envVars.PORT}`
210+
)
211+
212+
if (args.includes('--internal-actions')) {
213+
import('./interval').catch(err => {
214+
logger.error('Failed starting internal actions:', err)
215+
})
216+
}
217+
})
218+
219+
break
220+
}
221+
case 'internal': {
222+
await import('./interval')
223+
break
224+
}
225+
case 'db-init': {
226+
logger.info('Initializing a database...')
227+
initDb({ skipCreate: args.includes('--skip-create') }).catch(() => {
228+
logger.error('Failed to initialize database.')
229+
process.exit(1)
230+
})
231+
break
232+
}
208233
}
209234
}
210235

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+
}

0 commit comments

Comments
 (0)