slim admin backend image and remove compose config
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-14 13:15:21 +09:00
parent e811208773
commit 78f3578098
6 changed files with 68 additions and 36 deletions
+6 -2
View File
@@ -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
-21
View File
@@ -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:
+12 -8
View File
@@ -108,18 +108,22 @@ model UserLicense {
} }
model Organization { model Organization {
id String @id @default(uuid()) @db.Uuid id String @id @default(uuid()) @db.Uuid
name String name String
type OrganizationType @default(OTHER) type OrganizationType @default(OTHER)
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")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) createdById String? @map("created_by_id") @db.Uuid
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) createdByName String? @map("created_by_name")
affiliations UserAffiliation[] createdByEmail String? @map("created_by_email")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
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')
+36 -3
View File
@@ -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,
}, },
}); });