slim admin backend image and remove compose config
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
+6
-2
@@ -8,11 +8,15 @@ RUN npm run build
|
|||||||
FROM node:22-bookworm-slim AS runner
|
FROM node:22-bookworm-slim AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
ARG DOCKER_VERSION=27.5.1
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends ca-certificates curl git docker.io binutils \
|
&& apt-get install -y --no-install-recommends ca-certificates curl git \
|
||||||
|
&& curl -fsSL "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" \
|
||||||
|
| tar -xz -C /tmp \
|
||||||
|
&& install -m 0755 /tmp/docker/docker /usr/local/bin/docker \
|
||||||
&& curl -fsSL -o /usr/local/bin/kubectl "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl" \
|
&& curl -fsSL -o /usr/local/bin/kubectl "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl" \
|
||||||
&& chmod 0755 /usr/local/bin/kubectl \
|
&& chmod 0755 /usr/local/bin/kubectl \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /tmp/docker /var/lib/apt/lists/*
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci --omit=dev
|
RUN npm ci --omit=dev
|
||||||
COPY --from=builder /app/dist ./dist
|
COPY --from=builder /app/dist ./dist
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:17-alpine
|
|
||||||
container_name: atnmy-stdo-adm-v2-postgres
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: studio_admin
|
|
||||||
POSTGRES_USER: studio_admin
|
|
||||||
POSTGRES_PASSWORD: future1234!@
|
|
||||||
ports:
|
|
||||||
- "5436:5432"
|
|
||||||
volumes:
|
|
||||||
- atnmy-stdo-adm-v2-postgres:/var/lib/postgresql/data
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U studio_admin -d studio_admin"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 20
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
atnmy-stdo-adm-v2-postgres:
|
|
||||||
@@ -114,12 +114,16 @@ model Organization {
|
|||||||
domain String?
|
domain String?
|
||||||
defaultLicenseCode String? @map("default_license_code")
|
defaultLicenseCode String? @map("default_license_code")
|
||||||
isActive Boolean @default(true) @map("is_active")
|
isActive Boolean @default(true) @map("is_active")
|
||||||
|
createdById String? @map("created_by_id") @db.Uuid
|
||||||
|
createdByName String? @map("created_by_name")
|
||||||
|
createdByEmail String? @map("created_by_email")
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||||
affiliations UserAffiliation[]
|
affiliations UserAffiliation[]
|
||||||
|
|
||||||
@@unique([name, type])
|
@@unique([name, type])
|
||||||
@@index([isActive, type])
|
@@index([isActive, type])
|
||||||
|
@@index([createdByEmail])
|
||||||
@@map("organizations")
|
@@map("organizations")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF to_regclass('public.organizations') IS NOT NULL THEN
|
||||||
|
ALTER TABLE "organizations"
|
||||||
|
ADD COLUMN IF NOT EXISTS "created_by_id" UUID,
|
||||||
|
ADD COLUMN IF NOT EXISTS "created_by_name" TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS "created_by_email" TEXT;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
@@ -13,8 +13,11 @@ export class OrganizationsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
create(@Body() body: CreateOrganizationDto) {
|
create(
|
||||||
return this.organizationsService.create(body);
|
@Body() body: CreateOrganizationDto,
|
||||||
|
@Req() request: { admin?: { id: string; name: string; email: string } },
|
||||||
|
) {
|
||||||
|
return this.organizationsService.create(body, request.admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { Prisma } from '@prisma/auth-client';
|
import { Prisma } from '@prisma/auth-client';
|
||||||
|
|
||||||
|
import { getAdminAvatarUrl } from '../admins/admin-avatar';
|
||||||
import { AuthPrismaService } from '../auth-prisma/auth-prisma.service';
|
import { AuthPrismaService } from '../auth-prisma/auth-prisma.service';
|
||||||
import { CreateOrganizationDto, ListOrganizationsQuery, UpdateOrganizationDto, UpdateOrganizationLicenseDto } from './dto';
|
import { CreateOrganizationDto, ListOrganizationsQuery, UpdateOrganizationDto, UpdateOrganizationLicenseDto } from './dto';
|
||||||
|
|
||||||
|
type AdminRequester = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrganizationsService {
|
export class OrganizationsService {
|
||||||
constructor(private readonly authPrisma: AuthPrismaService) {}
|
constructor(
|
||||||
|
private readonly authPrisma: AuthPrismaService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async list(query: ListOrganizationsQuery = {}) {
|
async list(query: ListOrganizationsQuery = {}) {
|
||||||
const page = toPositiveInt(query.page, 1);
|
const page = toPositiveInt(query.page, 1);
|
||||||
@@ -37,6 +48,9 @@ export class OrganizationsService {
|
|||||||
type: true,
|
type: true,
|
||||||
domain: true,
|
domain: true,
|
||||||
defaultLicenseCode: true,
|
defaultLicenseCode: true,
|
||||||
|
createdById: true,
|
||||||
|
createdByName: true,
|
||||||
|
createdByEmail: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -51,6 +65,7 @@ export class OrganizationsService {
|
|||||||
organizations: organizations.map((organization, index) => ({
|
organizations: organizations.map((organization, index) => ({
|
||||||
...organization,
|
...organization,
|
||||||
defaultLicenseName: organization.defaultLicenseCode ? licenseNameByCode.get(organization.defaultLicenseCode) ?? organization.defaultLicenseCode : null,
|
defaultLicenseName: organization.defaultLicenseCode ? licenseNameByCode.get(organization.defaultLicenseCode) ?? organization.defaultLicenseCode : null,
|
||||||
|
createdByAvatarUrl: getAdminAvatarUrl(organization.createdById, this.configService),
|
||||||
no: total - ((page - 1) * limit + index),
|
no: total - ((page - 1) * limit + index),
|
||||||
})),
|
})),
|
||||||
total,
|
total,
|
||||||
@@ -59,13 +74,20 @@ export class OrganizationsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(body: CreateOrganizationDto) {
|
async create(body: CreateOrganizationDto, requester?: AdminRequester) {
|
||||||
try {
|
try {
|
||||||
const organization = await this.authPrisma.organization.create({
|
const organization = await this.authPrisma.organization.create({
|
||||||
data: {
|
data: {
|
||||||
name: body.name.trim(),
|
name: body.name.trim(),
|
||||||
type: body.type,
|
type: body.type,
|
||||||
domain: body.domain?.trim() || null,
|
domain: body.domain?.trim() || null,
|
||||||
|
...(requester
|
||||||
|
? {
|
||||||
|
createdById: requester.id,
|
||||||
|
createdByName: requester.name,
|
||||||
|
createdByEmail: requester.email,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
@@ -73,11 +95,19 @@ export class OrganizationsService {
|
|||||||
type: true,
|
type: true,
|
||||||
domain: true,
|
domain: true,
|
||||||
defaultLicenseCode: true,
|
defaultLicenseCode: true,
|
||||||
|
createdById: true,
|
||||||
|
createdByName: true,
|
||||||
|
createdByEmail: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { organization };
|
return {
|
||||||
|
organization: {
|
||||||
|
...organization,
|
||||||
|
createdByAvatarUrl: getAdminAvatarUrl(organization.createdById, this.configService),
|
||||||
|
},
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (
|
||||||
error instanceof Prisma.PrismaClientKnownRequestError &&
|
error instanceof Prisma.PrismaClientKnownRequestError &&
|
||||||
@@ -105,6 +135,9 @@ export class OrganizationsService {
|
|||||||
type: true,
|
type: true,
|
||||||
domain: true,
|
domain: true,
|
||||||
defaultLicenseCode: true,
|
defaultLicenseCode: true,
|
||||||
|
createdById: true,
|
||||||
|
createdByName: true,
|
||||||
|
createdByEmail: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user