diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..2426118
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,42 @@
+kind: pipeline
+type: ssh
+name: silancar-build-deploy
+
+server:
+ host:
+ from_secret: ssh_host
+ user:
+ from_secret: ssh_user
+ ssh_key:
+ from_secret: ssh_key
+
+steps:
+ - name: prepare repo
+ when:
+ branch:
+ - main
+ commands:
+ - rm -rf /opt/build/silancar
+ - mkdir -p /opt/build
+ - cd /opt/build
+ - git clone http://38.47.180.165:3000/humas/silancar.git
+
+ - name: build image
+ when:
+ branch:
+ - main
+ commands:
+ - docker login 38.47.180.165:3000 -u administrator -p HarborDockerImageRep0
+ - cd /opt/build/silancar
+ - docker build -t 38.47.180.165:3000/humas/silancar:$DRONE_BRANCH .
+ - docker push 38.47.180.165:3000/humas/silancar:$DRONE_BRANCH
+
+ - name: deploy
+ when:
+ branch:
+ - main
+ commands:
+ - docker pull 38.47.180.165:3000/humas/silancar:$DRONE_BRANCH
+ - docker stop web-silancar|| true
+ - docker rm web-silancar || true
+ - docker run -dt -p 4700:4000 --restart always --name web-silancar 38.47.180.165:3000/humas/silancar:$DRONE_BRANCH
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..5fc7ef3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,34 @@
+FROM node:23.5.0-alpine
+
+ENV PORT=4000
+ENV NODE_ENV=production
+ENV NODE_OPTIONS="--max-old-space-size=4096"
+
+RUN apk add --no-cache \
+ python3 \
+ make \
+ g++ \
+ git \
+ sqlite-dev
+
+WORKDIR /usr/src/app
+
+# Copy dependency files
+COPY package.json package-lock.json ./
+
+# Install dependencies
+RUN npm install --legacy-peer-deps
+
+# Copy source code
+COPY . .
+
+# Init DB (aman karena pakai || true)
+RUN mkdir -p ./lib/db
+RUN node scripts/init-db.js || true
+
+# Build Next.js
+RUN npm run build
+
+EXPOSE 4000
+
+CMD ["npm", "run", "start"]
diff --git a/app/auth/sign-in/page.tsx b/app/auth/sign-in/page.tsx
index f0442f7..51c7258 100644
--- a/app/auth/sign-in/page.tsx
+++ b/app/auth/sign-in/page.tsx
@@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import Link from "next/link"
import { useRouter } from "next/navigation"
+import { setCookiesEncrypt } from "@/utils/globals"
type FormValues = {
nrp: string
@@ -26,11 +27,14 @@ export default function SignInPage() {
})
const onSubmit = async (data: FormValues) => {
- console.log("DATA LOGIN:", data)
await new Promise((res) => setTimeout(res, 1000))
+ if(data.nrp=="12345678"&&data.password=="P@ssw0rd.1") {
+ setCookiesEncrypt("status", "Login", { expires: 1 });
+ router.push("/dashboard")
+ }
+
- router.push("/dashboard")
}
return (
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index a98bc5c..88c2187 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -1,8 +1,11 @@
+'use client'
import DashboardSideMenu from "@/components/layout/dashboard-side-menu"
import Footer from "@/components/layout/footer"
-import { PinIcon } from "lucide-react"
+import { getCookiesDecrypt } from "@/utils/globals"
import Image from "next/image"
import Link from "next/link"
+import { useRouter } from "next/navigation"
+import { useEffect } from "react"
const dummy = { name: "Andri2 Ferinata", plat: "B 14 QU" }
@@ -19,6 +22,15 @@ const menu = [
]
export default function Dashboard() {
+ const login=getCookiesDecrypt("status")
+ const router=useRouter()
+
+ useEffect(()=>{
+ if(!login){
+ router.push("/auth/sign-in")
+ }
+ },[login,router])
+
return (
diff --git a/app/favicon.ico b/app/favicon.ico
index 718d6fe..ae69595 100644
Binary files a/app/favicon.ico and b/app/favicon.ico differ
diff --git a/app/layout.tsx b/app/layout.tsx
index f6e8f18..1f9a9f5 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -3,6 +3,15 @@ import { Geist_Mono, Inter, Roboto } from "next/font/google"
import "./globals.css"
import { ThemeProvider } from "@/components/theme-provider"
import { cn } from "@/lib/utils"
+import type { Metadata } from "next"
+
+export const metadata: Metadata = {
+ title: "Silancar",
+ description: "Silancar Korlantas",
+ icons: {
+ icon: "/favicon.ico", // atau png
+ },
+}
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" })
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..ae69595
Binary files /dev/null and b/favicon.ico differ
diff --git a/package-lock.json b/package-lock.json
index 7d4c879..e5c8048 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,9 +8,13 @@
"name": "silancar",
"version": "0.0.1",
"dependencies": {
+ "@types/crypto-js": "^4.2.2",
+ "@types/js-cookie": "^3.0.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "crypto-js": "^4.2.0",
"embla-carousel-react": "^8.6.0",
+ "js-cookie": "^3.0.5",
"lucide-react": "^0.577.0",
"next": "16.1.7",
"next-themes": "^0.4.6",
@@ -3545,12 +3549,22 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/crypto-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
+ "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ=="
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true
},
+ "node_modules/@types/js-cookie": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
+ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ=="
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -4939,6 +4953,11 @@
"node": ">= 8"
}
},
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -7305,6 +7324,14 @@
"url": "https://github.com/sponsors/panva"
}
},
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/package.json b/package.json
index 272fd69..30af693 100644
--- a/package.json
+++ b/package.json
@@ -12,9 +12,13 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@types/crypto-js": "^4.2.2",
+ "@types/js-cookie": "^3.0.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "crypto-js": "^4.2.0",
"embla-carousel-react": "^8.6.0",
+ "js-cookie": "^3.0.5",
"lucide-react": "^0.577.0",
"next": "16.1.7",
"next-themes": "^0.4.6",
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..ae69595
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/utils/globals.tsx b/utils/globals.tsx
new file mode 100644
index 0000000..311028a
--- /dev/null
+++ b/utils/globals.tsx
@@ -0,0 +1,37 @@
+import Cookies from "js-cookie"
+import CryptoJS from "crypto-js"
+
+export function setCookiesEncrypt(
+ param: string,
+ data: T,
+ options?: Cookies.CookieAttributes
+) {
+ const cookiesEncrypt = CryptoJS.AES.encrypt(
+ JSON.stringify(data),
+ `${param}_EncryptKey@silancar`
+ ).toString()
+
+ Cookies.set(param, cookiesEncrypt, options)
+}
+
+export function getCookiesDecrypt(param: string): T | null {
+ const cookiesEncrypt = Cookies.get(param)
+
+ try {
+ if (cookiesEncrypt) {
+ const bytes = CryptoJS.AES.decrypt(
+ cookiesEncrypt,
+ `${param}_EncryptKey@silancar`
+ )
+
+ const decrypted = bytes.toString(CryptoJS.enc.Utf8)
+
+ return JSON.parse(decrypted) as T
+ }
+
+ return null
+ } catch (e) {
+ console.error("Decrypt error:", e)
+ return null
+ }
+}
\ No newline at end of file