skeet
Version:
Full-stack TypeScript Serverless Framework
252 lines (230 loc) • 8.13 kB
text/typescript
import fs from 'fs'
import { createHash } from 'crypto'
import { execSync } from 'child_process'
import { workerPluginList } from '@/types/skeetTypes'
import { skeetWorkerPluginList } from './workerPluginList'
export const KEYFILE_PATH = './keyfile.json'
export const GRAPHQL_PATH = './apps/api/src/graphql'
export const PRISMA_SCHEMA_PATH = './apps/api/prisma/schema.prisma'
export const API_PATH = './apps/api'
export const API_TYPE_PATH = API_PATH + '/src/types'
export const API_ENV_PRODUCTION_PATH = './apps/api/.env.production'
export const API_ENV_BUILD_PATH = './apps/api/.env.build'
export const WORKER_PATH = './apps/workers'
export const SKEET_CONFIG_PATH = './skeet-cloud.config.json'
export const ROUTE_PACKAGE_JSON_PATH = './package.json'
export const API_REPO_URL = 'https://github.com/elsoul/skeet-api'
export const WORKER_REPO_URL = 'https://github.com/elsoul/skeet-worker'
export const WORKER_SOLANA_TRANSFER_REPO_URL =
'https://github.com/elsoul/skeet-worker-solana-transfer'
export const ENUM_FILE_PATH = './apps/api/src/graphql'
export const FRONT_APP_REPO_URL = 'https://github.com/elsoul/skeet-app-template'
export const FRONT_APP_PATH = './apps/app'
export enum WorkerPlugins {
SOLANA_TRANSFER = 'solana-transfer',
ORCA_SWAP = 'orca-swap',
JUPITER_SWAP = 'jupiter-swap',
}
export const isWorkerPlugin = async (workerName: string) => {
if (!Object.values(WorkerPlugins)?.includes(workerName as WorkerPlugins)) {
return false
}
return true
}
export const getPluginData = async (pluginName: string) => {
try {
const plugin = skeetWorkerPluginList.filter(
(value) => value.name === pluginName
)
if (plugin.length === 0) return { id: 0, name: '', port: 0 }
return plugin[0]
} catch (error) {
console.log(`getPluginData: ${error}`)
return { id: 0, name: '', port: 0 }
}
}
export const getWorkerEnvPath = async (workerName: string) => {
return `${WORKER_PATH}/${workerName}/.env`
}
export const getWorkerName = async (appName: string, workerName: string) => {
return `skeet-${appName}-worker-${workerName}`
}
export const getWorkerEnvName = async (workerName: string) => {
const str = await getEnvString(`${workerName}_WORKER_URL`)
return str
}
export const getEnvString = async (str: string) => {
const output = str
.replace(/-/g, '_')
.replace(/([a-z])([A-Z])/g, '$1_$2')
.toUpperCase()
return output
}
export const genSecret = async (name: string) => {
return createHash('sha256').update(name).digest('hex')
}
export const defaultProductionEnvArray = [
'NO_PEER_DEPENDENCY_CHECK=1',
'DATABASE_URL=postgresql://postgres:${{ secrets.SKEET_GCP_DB_PASSWORD }}@${{ secrets.SKEET_GCP_DB_PRIVATE_IP }}:5432/skeet-${{ secrets.SKEET_APP_NAME }}-production?schema=public',
'SKEET_JWT_SALT=${{ secrets.SKEET_JWT_SALT }}',
'SKEET_BASE_URL=${{ secrets.SKEET_BASE_URL }}',
'SKEET_CRYPTO_SALT=${{ secrets.SKEET_CRYPTO_SALT }}',
'SKEET_PW_SALT=${{ secrets.SKEET_PW_SALT }}',
'SKEET_GCP_PROJECT_ID=${{ secrets.SKEET_GCP_PROJECT_ID }}',
'SKEET_GCP_TASK_REGION=${{ secrets.SKEET_GCP_TASK_REGION }}',
'GOOGLE_CLOUD_PROJECT=${{ secrets.SKEET_FB_PROJECT_ID }}',
'SKEET_API_ENDPOINT_URL=${{ secrets.SKEET_API_ENDPOINT_URL }}',
'TZ=${{ secrets.TZ }}',
]
export const getBuidEnvArray = async (
projectId: string,
fbProjectId: string,
databaseUrl: string,
tz: string
) => {
return [
'NO_PEER_DEPENDENCY_CHECK=1',
`SKEET_GCP_PROJECT_ID=${projectId}`,
`SKEET_FB_PROJECT_ID=${fbProjectId}`,
`TZ=${tz}`,
`DATABASE_URL=${databaseUrl}`,
]
}
export const getActionsEnvString = async (filePath: string) => {
const stream = fs.readFileSync(filePath)
const envArray: Array<string> = String(stream).split('\n')
let newEnv: Array<string> = []
for await (const envLine of envArray) {
let keyAndValue = envLine.match(/([A-Z_]+)="?([^"]*)"?/)
if (keyAndValue) {
if (keyAndValue[1].match('SKEET_')) continue
if (keyAndValue[1] === 'TZ') continue
const envString =
`${keyAndValue[1]}=$` + '{{ ' + `secrets.${keyAndValue[1]}` + ' }}'
newEnv.push(envString)
}
}
const returnArray = defaultProductionEnvArray.concat(newEnv)
return returnArray.join(',')
}
export const getBuidEnvString = async () => {
const stream = fs.readFileSync(API_ENV_PRODUCTION_PATH)
const envArray: Array<string> = String(stream).split('\n')
let hash: { [key: string]: string } = {}
for await (const line of envArray) {
const value = line.split('=')
hash[value[0]] = value[1]
}
const dabaseUrl = `postgresql://postgres:${hash['SKEET_GCP_DB_PASSWORD']}@${hash['SKEET_GCP_DB_PRIVATE_IP']}:5432/skeet-${hash['SKEET_APP_NAME']}-production?schema=public`
const buildEnvArray = await getBuidEnvArray(
hash['SKEET_GCP_PROJECT_ID'],
hash['GOOGLE_CLOUD_PROJECT'],
dabaseUrl,
hash['TZ']
)
const newEnv = envArray.filter((value) => {
if (
!value.match('SKEET_GCP_PROJECT_ID') &&
!value.match('SKEET_GCP_DB_PASSWORD')
) {
return value
}
})
const returnArray = buildEnvArray.concat(newEnv)
return returnArray.join(',')
}
export const getNetworkConfig = async (projectId: string, appName: string) => {
const skeetHeader = 'skeet-' + appName
return {
projectId,
appName,
cloudRunName: `skeet-${appName}-api`,
instanceName: skeetHeader + '-db',
networkName: skeetHeader + '-network',
firewallTcpName: skeetHeader + '-fw-tcp',
firewallSshName: skeetHeader + '-fw-ssh',
natName: skeetHeader + '-nat',
routerName: skeetHeader + '-router',
subnetName: skeetHeader + '-subnet',
connectorName: appName + '-con',
ipName: skeetHeader + '-external-ip',
loadBalancerIpName: skeetHeader + '-lb-ip',
ipRangeName: skeetHeader + '-ip-range',
serviceAccountName: `${appName}@${projectId}.iam.gserviceaccount.com`,
networkEndpointGroupName: `${skeetHeader}-neg`,
backendServiceName: `${skeetHeader}-bs`,
loadBalancerName: `${skeetHeader}-lb`,
sslName: `${skeetHeader}-ssl`,
proxyName: `${skeetHeader}-px`,
forwardingRuleName: `${skeetHeader}-fr`,
zoneName: `${skeetHeader}-zone`,
securityPolicyName: `${skeetHeader}-armor`,
}
}
export const getContainerImageUrl = async (
projectId: string,
appName: string,
region: string,
workerName: string = '',
isPlugin: boolean = false
) => {
const cRegion = await getContainerRegion(region)
let imageName = ''
if (workerName !== '' && isPlugin) {
imageName = 'skeet-worker-' + workerName
} else if (workerName !== '') {
imageName = 'skeet-' + appName + '-worker-' + workerName
} else {
imageName = 'skeet-' + appName + '-api'
}
let containerProjectName = isPlugin ? 'skeet-framework' : projectId
return cRegion + '/' + containerProjectName + '/' + imageName + ':latest'
}
export const getContainerImageName = async (
appName: string,
workerName: string = ''
) => {
const imageName =
workerName !== ''
? 'skeet-' + appName + '-worker-' + workerName
: 'skeet-' + appName + '-api'
return imageName
}
export const getContainerRegion = async (region: string) => {
switch (region) {
case region.match('asia')?.input:
return 'asia.gcr.io'
case region.match('eu')?.input:
return 'eu.gcr.io'
default:
return 'gcr.io'
}
}
export const regionToTimezone = async (region: string) => {
switch (true) {
case region.includes('asia'):
return 'Asia/Tokyo'
case region.includes('europe'):
return 'Europe/Amsterdam'
default:
return 'America/Los_Angeles'
}
}
export const getRunUrl = async (
projectId: string,
appName: string,
workerName: string = ''
) => {
try {
const runName =
workerName !== ''
? await getWorkerName(appName, workerName)
: (await getNetworkConfig(projectId, appName)).cloudRunName
console.log(runName)
const cmd = `gcloud run services list --project=${projectId} | grep ${runName} | awk '{print $4}'`
const res = String(execSync(cmd)).replace(/\r?\n/g, '')
return res
} catch (error) {
return ''
}
}