Skip to content

Commit

Permalink
Merge branch 'main' into aiproxy/admin
Browse files Browse the repository at this point in the history
  • Loading branch information
HUAHUAI23 committed Feb 13, 2025
2 parents 75a2c65 + 7257c6e commit 744b768
Show file tree
Hide file tree
Showing 14 changed files with 495 additions and 121 deletions.
1 change: 1 addition & 0 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ jobs:
tags: |
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=raw,value=${{ steps.prepare.outputs.tag_name }},enable=true
type=sha
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
Expand Down
15 changes: 12 additions & 3 deletions controllers/devbox/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ func main() {
var enablePodEnvMatcher bool
var enablePodPortMatcher bool
var enablePodEphemeralStorageMatcher bool

// config qps and burst
var configQPS int
var configBurst int
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
Expand Down Expand Up @@ -113,7 +115,9 @@ func main() {
flag.BoolVar(&enablePodEnvMatcher, "enable-pod-env-matcher", true, "If set, pod env matcher will be enabled")
flag.BoolVar(&enablePodPortMatcher, "enable-pod-port-matcher", true, "If set, pod port matcher will be enabled")
flag.BoolVar(&enablePodEphemeralStorageMatcher, "enable-pod-ephemeral-storage-matcher", false, "If set, pod ephemeral storage matcher will be enabled")

// config qps and burst
flag.IntVar(&configQPS, "config-qps", 50, "The qps of the config")
flag.IntVar(&configBurst, "config-burst", 100, "The burst of the config")
opts := zap.Options{
Development: true,
}
Expand Down Expand Up @@ -170,7 +174,12 @@ func main() {
"app.kubernetes.io/part-of": "devbox",
})

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
config := ctrl.GetConfigOrDie()
// set qps and burst to config qps and burst for kube-config
config.QPS = float32(configQPS)
config.Burst = configBurst

mgr, err := ctrl.NewManager(config, ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
Expand Down
10 changes: 8 additions & 2 deletions controllers/devbox/internal/controller/devbox_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
Expand Down Expand Up @@ -459,7 +460,9 @@ func (r *DevboxReconciler) deletePod(ctx context.Context, devbox *devboxv1alpha1
return err
}
// update commit history status because pod has been deleted
devbox.Status.LastTerminationState = pod.Status.ContainerStatuses[0].State
if len(pod.Status.ContainerStatuses) != 0 {
devbox.Status.LastTerminationState = pod.Status.ContainerStatuses[0].State
}
helper.UpdateCommitHistory(devbox, pod, true)
return nil
}
Expand All @@ -472,8 +475,10 @@ func (r *DevboxReconciler) handlePodDeleted(ctx context.Context, devbox *devboxv
return err
}
// update commit history status because pod has been deleted
if len(pod.Status.ContainerStatuses) != 0 {
devbox.Status.LastTerminationState = pod.Status.ContainerStatuses[0].State
}
helper.UpdateCommitHistory(devbox, pod, true)
devbox.Status.LastTerminationState = pod.Status.ContainerStatuses[0].State
return nil
}

Expand Down Expand Up @@ -606,6 +611,7 @@ func (r *DevboxReconciler) generateImageName(devbox *devboxv1alpha1.Devbox) stri
// SetupWithManager sets up the controller with the Manager.
func (r *DevboxReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
WithOptions(controller.Options{MaxConcurrentReconciles: 10}).
For(&devboxv1alpha1.Devbox{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Owns(&corev1.Pod{}, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})). // enqueue request if pod spec/status is updated
Owns(&corev1.Service{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Expand Down
2 changes: 1 addition & 1 deletion controllers/pkg/database/mongo/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (m *mongoDB) GetOwnersRecentUpdates(ownerList []string, checkTime time.Time
"owner": bson.M{"$in": ownerList},
"type": common.Consumption,
"app_type": bson.M{
"$ne": resources.AppType[resources.CVM],
"$nin": []int{int(resources.AppType[resources.CVM]), int(resources.AppType[resources.LLMToken])},
},
}

Expand Down
8 changes: 4 additions & 4 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ FROM deps AS builder
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1
ENV NEXT_TELEMETRY_DISABLED=1

# COPY --from=deps /app/packages ./packages

Expand All @@ -62,9 +62,9 @@ fi
# Production image, copy all the files and run next
FROM base AS runner

ENV NODE_ENV production
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
Expand Down Expand Up @@ -96,7 +96,7 @@ COPY --from=builder --chown=nextjs:nodejs /app/$path/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/$path/.next/static ./$path/.next/static
EXPOSE 3000

ENV PORT 3000
ENV PORT=3000

ENV launchpath=./${path}/server.js

Expand Down
19 changes: 18 additions & 1 deletion frontend/desktop/src/pages/api/account/enterpriseRealName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse, userUid: str
});

if (!info || !info.additionalInfo) {
return jsonRes(res, { code: 200, data: { authTimes: realNameAuthProvider.maxFailedTimes } });
return jsonRes(res, {
code: 200,
data: { remainingAttempts: realNameAuthProvider.maxFailedTimes }
});
}

const additionalInfo = info.additionalInfo as unknown as AdditionalInfo;
Expand Down Expand Up @@ -190,6 +193,20 @@ async function handlePost(

const data = validationResult.data;

const enterprise = await globalPrisma.enterpriseRealNameInfo.findFirst({
where: {
enterpriseName: data.keyName,
isVerified: true
}
});

if (enterprise) {
return jsonRes(res, {
code: 400,
message: 'Enterprise real name information has been used'
});
}

const globalToken = generateAuthenticationToken({
userUid: payload.userUid,
userId: payload.userId,
Expand Down
171 changes: 162 additions & 9 deletions frontend/desktop/src/pages/api/account/enterpriseRealNameVerify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { globalPrisma } from '@/services/backend/db/init';
import { z } from 'zod';
import { PAYMENTSTATUS } from '@/types/response/enterpriseRealName';
import { AdditionalInfo } from '@/types/response/enterpriseRealName';
import { RealNameAuthProvider } from '@/pages/api/account/faceIdRealNameAuthCallback';
import { TencentCloudFaceAuthConfig } from '@/pages/api/account/faceIdRealNameAuthCallback';
import { getInviterInfo } from '@/utils/getInviteInfo';

const schema = z.object({
transAmt: z.string().min(1)
Expand All @@ -25,7 +28,33 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const payload = await verifyAccessToken(req.headers);
if (!payload) return jsonRes(res, { code: 401, message: 'Token is invalid' });

const userUid = payload.userUid;
const userId = payload.userId;

const inviteInfo = await getInviterInfo(userId);

try {
const realNameAuthProvider: RealNameAuthProvider | null =
await globalPrisma.realNameAuthProvider.findFirst({
where: {
backend: 'TENCENTCLOUD',
authType: 'tcloudFaceAuth'
}
});

if (!realNameAuthProvider) {
throw new Error('faceidRealNameAuth: Real name authentication provider not found');
}

const config: TencentCloudFaceAuthConfig =
realNameAuthProvider.config as TencentCloudFaceAuthConfig;

if (!config) {
throw new Error('faceidRealNameAuth: Real name authentication configuration not found');
}

const realNameAuthReward = config.realNameAuthReward;

const validationResult = schema.safeParse(req.body);
if (!validationResult.success) {
return jsonRes(res, {
Expand Down Expand Up @@ -60,16 +89,129 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

const additionalInfo = info.additionalInfo as unknown as AdditionalInfo;

if (additionalInfo.transAmt === transAmt) {
await globalPrisma.enterpriseRealNameInfo.update({
where: { userUid: payload.userUid },
if (additionalInfo.transAmt !== transAmt) {
return jsonRes(res, {
code: 200,
message: 'transAmt_not_match',
data: {
isVerified: true,
additionalInfo: {
...additionalInfo,
paymentStatus: PAYMENTSTATUS.SUCCESS
authState: 'failed'
}
});
}

if (realNameAuthReward) {
await globalPrisma.$transaction(async (globalPrisma) => {
const currentAccount = await globalPrisma.account.findUniqueOrThrow({
where: { userUid }
});

if (!currentAccount.balance) {
throw new Error('enterpriseRealNameVerify: Account balance not found');
}

const currentActivityBonus = currentAccount.activityBonus || BigInt(0);
const realnameReward = BigInt(realNameAuthReward);

const newActivityBonus = currentActivityBonus + realnameReward;
const newBalance = currentAccount.balance + realnameReward;

const updatedAccount = await globalPrisma.account.update({
where: { userUid },
data: {
activityBonus: newActivityBonus,
balance: newBalance
}
});

const enterpriseRealNameInfo = await globalPrisma.enterpriseRealNameInfo.update({
where: { userUid: payload.userUid },
data: {
isVerified: true,
additionalInfo: {
...additionalInfo,
paymentStatus: PAYMENTSTATUS.SUCCESS
}
}
});

const accountTransaction = await globalPrisma.accountTransaction.create({
data: {
type: 'REALNAME_AUTH_REWARD',
userUid: userUid,
balance: realnameReward,
balance_before: currentAccount.balance,
deduction_balance: 0, // No deduction in this case
deduction_balance_before: currentAccount.deduction_balance,
message: 'Real name authentication reward',
billing_id: enterpriseRealNameInfo.id // You'll need to implement this function
}
});

await globalPrisma.userTask.updateMany({
where: {
userUid,
task: {
taskType: 'REAL_NAME_AUTH'
},
status: 'NOT_COMPLETED'
},
data: {
rewardStatus: 'COMPLETED',
status: 'COMPLETED',
completedAt: new Date()
}
});

if (inviteInfo.inviterId && inviteInfo.amount) {
const inviterUser = await globalPrisma.user.findUniqueOrThrow({
where: { id: inviteInfo.inviterId }
});

if (!inviterUser) {
throw new Error('enterpriseRealNameVerify: Inviter user not found');
}

const inviterAccount = await globalPrisma.account.findUniqueOrThrow({
where: { userUid: inviterUser.uid }
});

if (!inviterAccount.balance) {
throw new Error('enterpriseRealNameVerify: Inviter account balance not found');
}

const currentActivityBonus = inviterAccount.activityBonus || BigInt(0);
const realnameInviteReward = inviteInfo.amount;

const newActivityBonus = currentActivityBonus + realnameInviteReward;
const newBalance = inviterAccount.balance + realnameInviteReward;

await globalPrisma.account.update({
where: { userUid: inviterUser.uid },
data: {
activityBonus: newActivityBonus,
balance: newBalance
}
});

await globalPrisma.accountTransaction.create({
data: {
type: 'REALNAME_AUTH_INVITE_REWARD',
userUid: inviterUser.uid,
balance: realnameInviteReward,
balance_before: inviterAccount.balance,
deduction_balance: 0, // No deduction in this case
deduction_balance_before: inviterAccount.deduction_balance,
message: 'Real name authentication invite reward',
billing_id: enterpriseRealNameInfo.id // You'll need to implement this function
}
});
}

return {
account: updatedAccount,
transaction: accountTransaction,
enterpriseRealNameInfo: enterpriseRealNameInfo
};
});

return jsonRes(res, {
Expand All @@ -81,11 +223,22 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
}

await globalPrisma.enterpriseRealNameInfo.update({
where: { userUid: payload.userUid },
data: {
isVerified: true,
additionalInfo: {
...additionalInfo,
paymentStatus: PAYMENTSTATUS.SUCCESS
}
}
});

return jsonRes(res, {
code: 200,
message: 'transAmt_not_match',
data: {
authState: 'failed'
authState: 'success',
enterpriseRealName: additionalInfo.keyName
}
});
} catch (error) {
Expand Down
Loading

0 comments on commit 744b768

Please sign in to comment.