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";
|
import createNextIntlPlugin from "next-intl/plugin";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
|
output: 'standalone',
|
||||||
experimental: {
|
experimental: {
|
||||||
optimizePackageImports: ["@chakra-ui/react"],
|
optimizePackageImports: ["@chakra-ui/react"],
|
||||||
},
|
},
|
||||||
@@ -10,7 +11,7 @@ const nextConfig: NextConfig = {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
source: "/api/backend/:path*",
|
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==",
|
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rtsao/scc": "^1.1.0",
|
"@rtsao/scc": "^1.1.0",
|
||||||
"array-includes": "^3.1.9",
|
"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
|
* Import all services and hooks from this single file
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* import { authService, useLogin, adminRolesService, useGetAllRoles } from '@/lib/api/Example';
|
* import { authService, useLogin, adminRolesService, useGetAllRoles } from '@/lib/api/example';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Services
|
// 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