added sorvor admin page
Some checks failed
backend-admin-action / build-image (push) Failing after 8s
backend-action / build-image (push) Failing after 1m40s

This commit is contained in:
2024-05-20 16:48:06 +07:00
parent 98a65043c9
commit 23f37df217
14 changed files with 137 additions and 28 deletions

View File

@@ -4,6 +4,8 @@ Dockerfile
minio minio
node_modules node_modules
sqlite.db sqlite.db
sqlite.db-wal
sqlite.db-shm
.DS_Store .DS_Store
dist dist
.env .env

View File

@@ -0,0 +1,34 @@
name: backend-admin-action
run-name: ${{ gitea.actor }} is building docker image 🚀
on: [push]
jobs:
build-image:
runs-on: ubuntu-20.04
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
registry: gitea.cognizata.com
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: next.Dockerfile
push: true
platforms: linux/amd64
tags: gitea.cognizata.com/atapy/sorvor-admin:latest
target: app
- name: update server
uses: https://github.com/appleboy/ssh-action@v1.0.3
with:
host: 46.102.174.196
username: root
key: ${{ secrets.SORVOR_KEY }}
port: 22
script: "docker compose pull && docker compose up -d"
- run: echo "🍏 This job's status is ${{ job.status }}."

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useState } from "react";
import { JobCategory } from "./action"; import { JobCategory } from "./action";
type Props = { type Props = {

View File

@@ -10,7 +10,7 @@ export default async function Page() {
.findMany({ with: { zones: true } }) .findMany({ with: { zones: true } })
.execute(); .execute();
const jobList = await db.query.group.findMany().execute(); const jobList = await db.query.group.findMany().execute();
let r = await db.query.user const r = await db.query.user
.findMany({ columns: { id: true }, where: eq(user.verified, true) }) .findMany({ columns: { id: true }, where: eq(user.verified, true) })
.execute() .execute()
.then((v) => v.length); .then((v) => v.length);

View File

@@ -4,7 +4,7 @@ import { user } from "@/src/schema";
import { inArray } from "drizzle-orm"; import { inArray } from "drizzle-orm";
export async function saveUser(cids: string[]) { export async function saveUser(cids: string[]) {
let rs = await db const rs = await db
.update(user) .update(user)
.set({ verified: true }) .set({ verified: true })
.where(inArray(user.cid, cids)) .where(inArray(user.cid, cids))

View File

@@ -6,7 +6,7 @@ import IdComponent from "./IdComponent";
export default function Page() { export default function Page() {
const [idList, setIdList] = useState<string[]>([]); const [idList, setIdList] = useState<string[]>([]);
async function submit() { async function submit() {
let rs = await saveUser(idList); const rs = await saveUser(idList);
alert(`อัพเดทสำเร็จ ${rs.changes} คน`); alert(`อัพเดทสำเร็จ ${rs.changes} คน`);
} }
return ( return (

View File

@@ -1,3 +1,3 @@
export default function Page() { export default function Page() {
return <h1>Hello, Next.js!</h1>; return <h1>Hello!</h1>;
} }

View File

@@ -2,19 +2,11 @@ import { db } from "@/src/db";
import LocationSelector from "../../components/LocationSelector"; import LocationSelector from "../../components/LocationSelector";
import LocationContextProvider from "@/components/locationContext"; import LocationContextProvider from "@/components/locationContext";
import TotalSetter from "./TotalSetter"; import TotalSetter from "./TotalSetter";
import { eq } from "drizzle-orm";
import { user } from "@/src/schema";
export default async function Page() { export default async function Page() {
const provinces = await db.query.province const provinces = await db.query.province
.findMany({ with: { zones: true } }) .findMany({ with: { zones: true } })
.execute(); .execute();
const jobList = await db.query.group.findMany().execute();
let r = await db.query.user
.findMany({ columns: { id: true }, where: eq(user.verified, true) })
.execute()
.then((v) => v.length);
console.log(r);
return ( return (
<LocationContextProvider> <LocationContextProvider>
<LocationSelector provinces={provinces} /> <LocationSelector provinces={provinces} />

View File

@@ -13,6 +13,16 @@ services:
- ./sqlite.db-wal:/app/sqlite.db-wal - ./sqlite.db-wal:/app/sqlite.db-wal
env_file: env_file:
- .env - .env
admin:
build:
dockerfile: ./next.Dockerfile
ports:
- 3003:3000
volumes:
- ./sqlite.db:/app/sqlite.db
- ./sqlite.db-shm:/app/sqlite.db-shm
- ./sqlite.db-wal:/app/sqlite.db-wal
frontend: frontend:
restart: always restart: always
image: gitea.cognizata.com/atapy/sorvor-front image: gitea.cognizata.com/atapy/sorvor-front
@@ -50,13 +60,6 @@ services:
- ./caddy/config/:/config - ./caddy/config/:/config
- ./caddy/logs/:/var/log/caddy/ - ./caddy/logs/:/var/log/caddy/
sqliteweb:
image: tomdesinto/sqliteweb
ports:
- 8080:8080
volumes:
- ./sqlite.db:/db/sqlite.db
command: sqlite.db
crowdsec: crowdsec:
image: crowdsecurity/crowdsec image: crowdsecurity/crowdsec
environment: environment:

68
next.Dockerfile Normal file
View File

@@ -0,0 +1,68 @@
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 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
RUN \
if [ -f yarn.lock ]; then yarn run next-build; \
elif [ -f package-lock.json ]; then npm run next-build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run next-build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle
USER nextjs
EXPOSE 3000
ENV PORT 3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

3
next.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
output: "standalone",
};

View File

@@ -7,6 +7,7 @@
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon --exec ts-node --swc src/app.ts", "dev": "nodemon --exec ts-node --swc src/app.ts",
"next-dev": "next dev", "next-dev": "next dev",
"next-build": "next build",
"start": "node dist/src/app.js", "start": "node dist/src/app.js",
"build": "swc src -d dist", "build": "swc src -d dist",
"lint": "next lint", "lint": "next lint",

View File

@@ -6,17 +6,17 @@ import { z } from "zod";
export const adminRoute = router({ export const adminRoute = router({
totalUser: publicProcedure.query(async () => { totalUser: publicProcedure.query(async () => {
let rs = await db const rs = await db
.select({ zone: user.zone, value: count(user.id) }) .select({ zone: user.zone, value: count(user.id) })
.from(user) .from(user)
.groupBy(user.zone) .groupBy(user.zone)
.execute(); .execute();
let zones = await db.query.zone const zones = await db.query.zone
.findMany({ with: { province: true } }) .findMany({ with: { province: true } })
.execute(); .execute();
zones.sort((a, b) => a.province.id - b.province.id); zones.sort((a, b) => a.province.id - b.province.id);
let summary = zones.map((z) => { const summary = zones.map((z) => {
let num = rs.find((user) => user.zone == z.id)?.value ?? 0; const num = rs.find((user) => user.zone == z.id)?.value ?? 0;
return { return {
count: num, count: num,
zone: z.name, zone: z.name,
@@ -32,17 +32,17 @@ export const adminRoute = router({
if (input.key !== "3RJjV7Hseo2xiJoVta/x2AJIGw5EK+a5nAwtnAjw37U=") { if (input.key !== "3RJjV7Hseo2xiJoVta/x2AJIGw5EK+a5nAwtnAjw37U=") {
return "Invalid Key"; return "Invalid Key";
} }
let thisUser = await db.query.user const thisUser = await db.query.user
.findFirst({ where: eq(user.cid, input.cid) }) .findFirst({ where: eq(user.cid, input.cid) })
.execute(); .execute();
if (thisUser === undefined) { if (thisUser === undefined) {
return "User not found"; return "User not found";
} }
let uoresult = await db const uoresult = await db
.delete(userOpinion) .delete(userOpinion)
.where(eq(userOpinion.userId, thisUser.id)) .where(eq(userOpinion.userId, thisUser.id))
.execute(); .execute();
let rs = await db.delete(user).where(eq(user.cid, input.cid)).execute(); const rs = await db.delete(user).where(eq(user.cid, input.cid)).execute();
return { useropinion: uoresult, rs }; return { useropinion: uoresult, rs };
}), }),
}); });

View File

@@ -109,6 +109,12 @@
} }
] ]
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": [
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
"**/*.tsx",
"next.config.js"
],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }