mirror of
https://github.com/biersoeckli/QuickStack.git
synced 2026-01-02 01:30:38 -06:00
created init service for deployment
This commit is contained in:
@@ -39,6 +39,9 @@ RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
RUN mkdir storage
|
||||
RUN chown nextjs:nodejs storage
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
|
||||
|
||||
@@ -33,7 +33,7 @@ generator zod {
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:../db/data.db"
|
||||
url = "file:../storage/db/data.db"
|
||||
}
|
||||
|
||||
// *** The following code is for the default NextAuth.js schema
|
||||
|
||||
@@ -2,28 +2,51 @@ import { createServer } from 'http'
|
||||
import { parse } from 'url'
|
||||
import next from 'next'
|
||||
import socketIoServer from './socket-io.server'
|
||||
import initService from './server/services/init.service'
|
||||
import { CommandExecutorUtils } from './server/utils/command-executor.utils'
|
||||
|
||||
// Source: https://nextjs.org/docs/app/building-your-application/configuring/custom-server
|
||||
|
||||
const port = parseInt(process.env.PORT || '3000', 10)
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
const app = next({ dev })
|
||||
const handle = app.getRequestHandler()
|
||||
|
||||
app.prepare().then(() => {
|
||||
async function setupQuickStack() {
|
||||
console.log('Setting up QuickStack...');
|
||||
await initService.initializeQuickStack();
|
||||
}
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const parsedUrl = parse(req.url!, true)
|
||||
handle(req, res, parsedUrl)
|
||||
async function initializeNextJs() {
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// update database
|
||||
console.log('Running db migration...');
|
||||
await CommandExecutorUtils.runCommand('npx prisma migrate deploy');
|
||||
}
|
||||
|
||||
const app = next({ dev })
|
||||
const handle = app.getRequestHandler()
|
||||
|
||||
app.prepare().then(() => {
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const parsedUrl = parse(req.url!, true)
|
||||
handle(req, res, parsedUrl)
|
||||
});
|
||||
|
||||
socketIoServer.initialize(server);
|
||||
server.listen(port)
|
||||
|
||||
console.log(
|
||||
`> Server listening at http://localhost:${port} as ${dev ? 'development' : process.env.NODE_ENV
|
||||
}`
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
socketIoServer.initialize(server);
|
||||
|
||||
server.listen(port)
|
||||
|
||||
console.log(
|
||||
`> Server listening at http://localhost:${port} as ${dev ? 'development' : process.env.NODE_ENV
|
||||
}`
|
||||
)
|
||||
});
|
||||
if (process.env.NODE_ENV === 'production' && process.env.START_MODE === 'setup') {
|
||||
setupQuickStack();
|
||||
} else {
|
||||
initializeNextJs();
|
||||
}
|
||||
|
||||
|
||||
135
src/server/services/init.service.ts
Normal file
135
src/server/services/init.service.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import k3s from "../adapter/kubernetes-api.adapter";
|
||||
import { V1Deployment, V1Service } from "@kubernetes/client-node";
|
||||
import namespaceService from "./namespace.service";
|
||||
import { StringUtils } from "../utils/string.utils";
|
||||
|
||||
class InitService {
|
||||
|
||||
private readonly QUICKSTACK_NAMESPACE = 'quickstack';
|
||||
private readonly QUICKSTACK_DEPLOYMENT_NAME = 'quickstack';
|
||||
|
||||
|
||||
async initializeQuickStack() {
|
||||
await namespaceService.createNamespaceIfNotExists(this.QUICKSTACK_NAMESPACE)
|
||||
await this.deleteExistingDeployment();
|
||||
await this.createOrUpdatePvc();
|
||||
await this.createDeployment();
|
||||
await this.createOrUpdateService(true);
|
||||
console.log('QuickStack successfully initialized');
|
||||
}
|
||||
|
||||
async createOrUpdateService(openNodePort = false) {
|
||||
const serviceName = StringUtils.toServiceName(this.QUICKSTACK_DEPLOYMENT_NAME);
|
||||
const body: V1Service = {
|
||||
metadata: {
|
||||
name: StringUtils.toServiceName(this.QUICKSTACK_DEPLOYMENT_NAME)
|
||||
},
|
||||
spec: {
|
||||
selector: {
|
||||
app: this.QUICKSTACK_DEPLOYMENT_NAME
|
||||
},
|
||||
ports: [{
|
||||
protocol: 'TCP',
|
||||
port: 3000,
|
||||
targetPort: 3000,
|
||||
nodePort: openNodePort ? 3000 : undefined,
|
||||
}]
|
||||
}
|
||||
};
|
||||
const existingService = await k3s.core.readNamespacedService(serviceName, this.QUICKSTACK_NAMESPACE);
|
||||
if (existingService.body) {
|
||||
console.warn('Service already exists, updating');
|
||||
return k3s.core.replaceNamespacedService(serviceName, this.QUICKSTACK_NAMESPACE, body);
|
||||
} else {
|
||||
console.warn('Service does not exist, creating');
|
||||
return k3s.core.createNamespacedService(this.QUICKSTACK_NAMESPACE, body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async createOrUpdatePvc() {
|
||||
const pvcName = StringUtils.toPvcName(this.QUICKSTACK_DEPLOYMENT_NAME);
|
||||
const pvc = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'PersistentVolumeClaim',
|
||||
metadata: {
|
||||
name: pvcName,
|
||||
namespace: this.QUICKSTACK_NAMESPACE
|
||||
},
|
||||
spec: {
|
||||
accessModes: ['ReadWriteOnce'],
|
||||
storageClassName: 'longhorn',
|
||||
resources: {
|
||||
requests: {
|
||||
storage: '1Gi'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const existingPvc = await k3s.core.readNamespacedPersistentVolumeClaim(pvcName, this.QUICKSTACK_NAMESPACE);
|
||||
if (existingPvc.body) {
|
||||
console.warn('PVC already exists, updating');
|
||||
await k3s.core.replaceNamespacedPersistentVolumeClaim(pvcName, this.QUICKSTACK_NAMESPACE, pvc);
|
||||
} else {
|
||||
console.warn('PVC does not exist, creating');
|
||||
await k3s.core.createNamespacedPersistentVolumeClaim(this.QUICKSTACK_NAMESPACE, pvc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async createDeployment() {
|
||||
const body: V1Deployment = {
|
||||
metadata: {
|
||||
name: this.QUICKSTACK_DEPLOYMENT_NAME,
|
||||
},
|
||||
spec: {
|
||||
replicas: 1,
|
||||
selector: {
|
||||
matchLabels: {
|
||||
app: this.QUICKSTACK_DEPLOYMENT_NAME
|
||||
}
|
||||
},
|
||||
template: {
|
||||
metadata: {
|
||||
labels: {
|
||||
app: this.QUICKSTACK_DEPLOYMENT_NAME
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
containers: [
|
||||
{
|
||||
name: this.QUICKSTACK_DEPLOYMENT_NAME,
|
||||
image: 'quickstack/quickstack:latest',
|
||||
imagePullPolicy: 'Always',
|
||||
volumeMounts: [{
|
||||
name: 'quickstack-volume',
|
||||
mountPath: '/app/storage'
|
||||
}]
|
||||
}
|
||||
],
|
||||
volumes: [{
|
||||
name: 'quickstack-volume',
|
||||
persistentVolumeClaim: {
|
||||
claimName: StringUtils.toPvcName(this.QUICKSTACK_DEPLOYMENT_NAME)
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
await k3s.apps.createNamespacedDeployment(this.QUICKSTACK_NAMESPACE, body);
|
||||
console.log('Deployment created');
|
||||
}
|
||||
|
||||
private async deleteExistingDeployment() {
|
||||
const existingDeployments = await k3s.apps.readNamespacedDeployment(this.QUICKSTACK_DEPLOYMENT_NAME, this.QUICKSTACK_NAMESPACE);
|
||||
const quickStackAlreadyDeployed = !!existingDeployments.body;
|
||||
if (quickStackAlreadyDeployed) {
|
||||
console.warn('QuickStack already deployed, deleting existing deployment (data wont be lost)');
|
||||
await k3s.apps.deleteNamespacedDeployment(this.QUICKSTACK_DEPLOYMENT_NAME, this.QUICKSTACK_NAMESPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initService = new InitService();
|
||||
export default initService;
|
||||
@@ -49,7 +49,7 @@ export class StringUtils {
|
||||
return `svc-${appId}`;
|
||||
}
|
||||
|
||||
static toPvcName(volumeId: string) {
|
||||
static toPvcName(volumeId: string): `pvc-${string}` {
|
||||
return `pvc-${volumeId}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user