This commit is contained in:
45
.github/workflows/deploy-ui.yml
vendored
Normal file
45
.github/workflows/deploy-ui.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Deploy Frontend
|
||||
run-name: ${{ gitea.actor }} is deploying frontend 🚀
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build and Push Docker Image
|
||||
run: |
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_API_URL='${{ secrets.NEXT_PUBLIC_API_URL }}' \
|
||||
--build-arg NEXT_PUBLIC_AUTH_REQUIRED='false' \
|
||||
-t skript-ui .
|
||||
|
||||
- name: Stop and Remove Existing Container
|
||||
run: |
|
||||
docker stop ui-skript-container || true
|
||||
docker rm ui-skript-container || true
|
||||
|
||||
- name: Run New Container
|
||||
run: |
|
||||
docker run -d \
|
||||
--name ui-skript-container \
|
||||
--restart unless-stopped \
|
||||
--network gitea-server_gitea \
|
||||
-p 1506:3000 \
|
||||
-e NEXTAUTH_URL='${{ secrets.NEXTAUTH_URL }}' \
|
||||
-e NEXTAUTH_SECRET='${{ secrets.NEXTAUTH_SECRET }}' \
|
||||
-e INTERNAL_API_URL='http://backend-skript-container:3000' \
|
||||
skript-ui
|
||||
|
||||
- name: Cleanup
|
||||
run: docker image prune -f
|
||||
46
Dockerfile
Normal file
46
Dockerfile
Normal file
@@ -0,0 +1,46 @@
|
||||
# Base image
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Pass build args as environment variables
|
||||
ARG NEXT_PUBLIC_API_URL
|
||||
ARG NEXT_PUBLIC_AUTH_REQUIRED
|
||||
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||
ENV NEXT_PUBLIC_AUTH_REQUIRED=${NEXT_PUBLIC_AUTH_REQUIRED}
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# 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
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT=3000
|
||||
# set hostname to localhost
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -2,6 +2,7 @@ import type { NextConfig } from "next";
|
||||
import createNextIntlPlugin from "next-intl/plugin";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
experimental: {
|
||||
optimizePackageImports: ["@chakra-ui/react"],
|
||||
},
|
||||
@@ -10,7 +11,7 @@ const nextConfig: NextConfig = {
|
||||
return [
|
||||
{
|
||||
source: "/api/backend/:path*",
|
||||
destination: "http://localhost:3000/api/:path*",
|
||||
destination: `${process.env.INTERNAL_API_URL || "http://localhost:3000"}/api/:path*`,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -5983,7 +5983,6 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { apiRequest } from "@/lib/api/api-service";
|
||||
import { ApiResponse } from "@/types/api-response";
|
||||
import { GenerateScriptDto, GeneratedScriptResponse } from "./types";
|
||||
|
||||
const generateScript = (projectId: string, data: GenerateScriptDto) => {
|
||||
return apiRequest<ApiResponse<GeneratedScriptResponse>>({
|
||||
url: `/projects/${projectId}/writer/generate`,
|
||||
client: "core",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const aiWriterService = {
|
||||
generateScript,
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
export interface GenerateScriptDto {
|
||||
topic: string;
|
||||
contentType: string;
|
||||
targetAudience: string[];
|
||||
speechStyle: string[];
|
||||
targetDuration: string;
|
||||
tone: string;
|
||||
language: string;
|
||||
includeInterviews?: boolean;
|
||||
}
|
||||
|
||||
export interface GeneratedScriptResponse {
|
||||
script: any[]; // ScriptSegment[]
|
||||
seo: any;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { aiWriterService } from "./service";
|
||||
import { GenerateScriptDto } from "./types";
|
||||
|
||||
export function useGenerateScript() {
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, data }: { projectId: string; data: GenerateScriptDto }) =>
|
||||
aiWriterService.generateScript(projectId, data),
|
||||
});
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* Import all services and hooks from this single file
|
||||
*
|
||||
* Usage:
|
||||
* import { authService, useLogin, adminRolesService, useGetAllRoles } from '@/lib/api/Example';
|
||||
* import { authService, useLogin, adminRolesService, useGetAllRoles } from '@/lib/api/example';
|
||||
*/
|
||||
|
||||
// Services
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { apiRequest } from "@/lib/api/api-service";
|
||||
import { ApiResponse } from "@/types/api-response";
|
||||
import { CreateProjectDto, Project, UpdateProjectDto } from "./types";
|
||||
|
||||
const getMyProjects = () => {
|
||||
return apiRequest<ApiResponse<Project[]>>({
|
||||
url: "/projects/my-projects",
|
||||
client: "core", // or whatever specific client config
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
const createProject = (data: CreateProjectDto) => {
|
||||
return apiRequest<ApiResponse<Project>>({
|
||||
url: "/projects",
|
||||
client: "core",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
};
|
||||
const getProject = (id: string) => {
|
||||
return apiRequest<ApiResponse<Project>>({
|
||||
url: `/projects/${id}`,
|
||||
client: "core",
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
const updateProject = (id: string, data: UpdateProjectDto) => {
|
||||
return apiRequest<ApiResponse<Project>>({
|
||||
url: `/projects/${id}`,
|
||||
client: "core",
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
const deleteProject = (id: string) => {
|
||||
return apiRequest<ApiResponse<Project>>({
|
||||
url: `/projects/${id}`,
|
||||
client: "core",
|
||||
method: "delete",
|
||||
});
|
||||
};
|
||||
|
||||
export const projectsService = {
|
||||
getMyProjects,
|
||||
createProject,
|
||||
getProject,
|
||||
updateProject,
|
||||
deleteProject,
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
export interface Project {
|
||||
id: string;
|
||||
userId: string;
|
||||
topic: string;
|
||||
contentType: string;
|
||||
language: string;
|
||||
targetDuration: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
// ... other fields matching backend Project model
|
||||
}
|
||||
|
||||
export interface CreateProjectDto {
|
||||
topic: string;
|
||||
contentType: string;
|
||||
targetAudience: string[];
|
||||
speechStyle: string[];
|
||||
targetDuration: string;
|
||||
tone: string;
|
||||
language: string;
|
||||
includeInterviews?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateProjectDto extends Partial<CreateProjectDto> { }
|
||||
@@ -1,48 +0,0 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { projectsService } from "./service";
|
||||
import { CreateProjectDto, UpdateProjectDto } from "./types";
|
||||
|
||||
export const ProjectsQueryKeys = {
|
||||
all: ["projects"] as const,
|
||||
myProjects: () => [...ProjectsQueryKeys.all, "my-projects"] as const,
|
||||
detail: (id: string) => [...ProjectsQueryKeys.all, "detail", id] as const,
|
||||
};
|
||||
|
||||
export function useMyProjects() {
|
||||
return useQuery({
|
||||
queryKey: ProjectsQueryKeys.myProjects(),
|
||||
queryFn: () => projectsService.getMyProjects(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useProject(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ProjectsQueryKeys.detail(id),
|
||||
queryFn: () => projectsService.getProject(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateProject() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateProjectDto) => projectsService.createProject(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ProjectsQueryKeys.myProjects() });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateProject() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateProjectDto }) =>
|
||||
projectsService.updateProject(id, data),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ProjectsQueryKeys.detail(variables.id) });
|
||||
queryClient.invalidateQueries({ queryKey: ProjectsQueryKeys.myProjects() });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user