Compare commits

..

No commits in common. "65a34a627f81d0d10e69ca9a21ced5491500edf5" and "8e7f3b211e76a45efad21a569ad9a707e574e067" have entirely different histories.

6 changed files with 81 additions and 78 deletions

View File

@ -28,6 +28,16 @@ steps:
git checkout ${DRONE_TAG}
fi
- name: frontend-deps
image: node:22-alpine
commands:
- cd apps/frontend && corepack enable && corepack prepare pnpm@10 --activate && pnpm install --frozen-lockfile
- name: frontend-build
image: node:22-alpine
commands:
- cd apps/frontend && pnpm build
- name: cargo-build
image: rust:1.94-bookworm
commands:
@ -70,7 +80,7 @@ steps:
/kaniko/executor --context . --dockerfile docker/frontend.Dockerfile \
--destination ${REGISTRY}/frontend:${TAG} --destination ${REGISTRY}/frontend:latest
echo "==> All images pushed"
depends_on: [ cargo-build ]
depends_on: [ cargo-build, frontend-build ]
- name: prepare-kubeconfig
image: alpine:latest

View File

@ -84,42 +84,6 @@ spec:
name: {{ include "gitdata.fullname" . }}-config
key: APP_AVATAR_PATH
optional: true
- name: APP_SMTP_HOST
valueFrom:
configMapKeyRef:
name: {{ include "gitdata.fullname" . }}-config
key: APP_SMTP_HOST
optional: true
- name: APP_SMTP_PORT
valueFrom:
configMapKeyRef:
name: {{ include "gitdata.fullname" . }}-config
key: APP_SMTP_PORT
optional: true
- name: APP_SMTP_USERNAME
valueFrom:
configMapKeyRef:
name: {{ include "gitdata.fullname" . }}-config
key: APP_SMTP_USERNAME
optional: true
- name: APP_SMTP_PASSWORD
valueFrom:
configMapKeyRef:
name: {{ include "gitdata.fullname" . }}-config
key: APP_SMTP_PASSWORD
optional: true
- name: APP_SMTP_FROM
valueFrom:
configMapKeyRef:
name: {{ include "gitdata.fullname" . }}-config
key: APP_SMTP_FROM
optional: true
- name: APP_SMTP_TLS
valueFrom:
configMapKeyRef:
name: {{ include "gitdata.fullname" . }}-config
key: APP_SMTP_TLS
optional: true
{{- range .Values.app.env }}
- name: {{ .name }}
value: {{ .value | quote }}

View File

@ -1,8 +1,26 @@
# Runtime only — frontend built externally via pnpm
FROM nginx:alpine
# ---- Stage 1: Build ----
FROM node:22-alpine AS builder
# Copy pre-built SPA assets
COPY apps/frontend/dist /usr/share/nginx/html
WORKDIR /app
# Copy package files
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && corepack prepare pnpm@10 --activate
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source
COPY . .
# Build
RUN pnpm build
# ---- Stage 2: Serve with nginx ----
FROM nginx:alpine AS runtime
# Copy built assets
COPY --from=builder /app/dist /usr/share/nginx/html
# nginx configuration for SPA
RUN echo 'server { \
@ -28,4 +46,5 @@ RUN echo 'server { \
}' > /etc/nginx/conf.d/default.conf
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]

View File

@ -19,7 +19,9 @@ impl MigrationTrait for Migration {
)
.await?;
let sql = "DROP INDEX IF EXISTS idx_room_message_content_tsv;
let sql = "DROP TRIGGER IF EXISTS room_message_tsv_update ON room_message;
DROP FUNCTION IF EXISTS room_message_tsv_trigger;
DROP INDEX IF EXISTS idx_room_message_content_tsv;
ALTER TABLE room_message DROP COLUMN IF EXISTS content_tsv;";
super::execute_sql(manager, sql).await
}

View File

@ -15,9 +15,34 @@ CREATE INDEX IF NOT EXISTS idx_room_message_reaction_message ON room_message_rea
CREATE INDEX IF NOT EXISTS idx_room_message_reaction_user ON room_message_reaction ("user");
CREATE INDEX IF NOT EXISTS idx_room_message_reaction_room ON room_message_reaction (room);
-- Add full-text search column for message content
-- Add full-text search index for message content (PostgreSQL tsvector)
ALTER TABLE room_message
ADD COLUMN IF NOT EXISTS content_tsv TSVECTOR;
-- Update existing messages with tsvector
UPDATE room_message
SET content_tsv = to_tsvector('english', content)
WHERE content_tsv IS NULL;
-- Create index for full-text search
CREATE INDEX IF NOT EXISTS idx_room_message_content_tsv ON room_message USING GIN (content_tsv);
-- Trigger to auto-update content_tsv on insert/update
CREATE OR REPLACE FUNCTION room_message_tsv_trigger() RETURNS TRIGGER AS
$$
BEGIN
NEW.content_tsv := to_tsvector('simple', COALESCE(NEW.content, ''));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER room_message_tsv_update
BEFORE INSERT OR UPDATE
ON room_message
FOR EACH ROW
EXECUTE FUNCTION room_message_tsv_trigger();
-- Backfill existing rows
UPDATE room_message
SET content_tsv = to_tsvector('simple', content)
WHERE content_tsv IS NULL;

View File

@ -4,8 +4,7 @@
*
* Workflow:
* 1. cargo build --release --target x86_64-unknown-linux-gnu (compile Rust binaries)
* 2. pnpm build (build frontend SPA)
* 3. docker build (copy pre-built artifacts into minimal runtime images)
* 2. docker build (copy pre-built binaries into minimal runtime images)
*
* Usage:
* node scripts/build.js # Build all images
@ -19,21 +18,19 @@
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const REGISTRY = process.env.REGISTRY || 'harbor.gitdata.me/gta_team';
const TAG = process.env.TAG || new Date().toISOString().slice(0, 13).replace('T', '-');
const BUILD_TARGET = process.env.TARGET || 'x86_64-unknown-linux-gnu';
const RUST_SERVICES = ['app', 'gitserver', 'email-worker', 'git-hook', 'operator', 'static'];
const FRONTEND_SERVICE = 'frontend';
const ALL_SERVICES = [...RUST_SERVICES, FRONTEND_SERVICE];
const ALL_SERVICES = [...RUST_SERVICES, 'frontend'];
const args = process.argv.slice(2);
const targets = args.length > 0 ? args : ALL_SERVICES;
// Determine which rust services are in targets
const rustTargets = targets.filter(s => RUST_SERVICES.includes(s));
const needsFrontend = targets.includes(FRONTEND_SERVICE);
console.log(`\n=== Build Configuration ===`);
console.log(`Registry: ${REGISTRY}`);
@ -45,15 +42,21 @@ console.log(`Services: ${targets.join(', ')}\n`);
if (rustTargets.length > 0) {
console.log(`\n==> Step 1: Building Rust binaries`);
const cpus = require('os').cpus().length;
const packages = rustTargets.map(s => {
const pkgMap = {
'app': 'app',
'gitserver': 'gitserver',
'email-worker': 'email-server',
'git-hook': 'git-hook',
'operator': 'operator',
'static': 'static-server',
};
return `--package ${pkgMap[s]}`;
}).join(' ');
try {
execSync(
`cargo build --release --target ${BUILD_TARGET} ${rustTargets.map(s => {
const pkgMap = {
'app': 'app', 'gitserver': 'gitserver', 'email-worker': 'email-server',
'git-hook': 'git-hook', 'operator': 'operator', 'static': 'static-server',
};
return `--package ${pkgMap[s]}`;
}).join(' ')} -j ${cpus}`,
`cargo build --release --target ${BUILD_TARGET} ${packages} -j ${cpus}`,
{ stdio: 'inherit', cwd: path.join(__dirname, '..') }
);
console.log(` [OK] Rust binaries built`);
@ -63,28 +66,8 @@ if (rustTargets.length > 0) {
}
}
// Step 2: Build frontend (requires apps/frontend to exist locally)
if (needsFrontend) {
const frontendDir = path.join(__dirname, '..', 'apps', 'frontend');
if (!fs.existsSync(frontendDir)) {
console.error(`\n [FAIL] apps/frontend not found. Please ensure frontend source exists.`);
process.exit(1);
}
console.log(`\n==> Step 2: Building frontend`);
try {
execSync(`corepack enable && corepack prepare pnpm@10 --activate && pnpm install --frozen-lockfile && pnpm build`, {
stdio: 'inherit',
cwd: frontendDir,
});
console.log(` [OK] Frontend built`);
} catch (error) {
console.error(` [FAIL] Frontend build failed`);
process.exit(1);
}
}
// Step 3: Build Docker images
console.log(`\n==> Step 3: Building Docker images`);
// Step 2: Build Docker images
console.log(`\n==> Step 2: Building Docker images`);
for (const service of targets) {
if (!ALL_SERVICES.includes(service)) {