Compare commits
10 Commits
v1.0.7
..
0283c293ef
| Author | SHA1 | Date | |
|---|---|---|---|
|
0283c293ef
|
|||
| 8d75849c55 | |||
| 58b44dddad | |||
| c9de531389 | |||
| d48cfe12f2 | |||
| 3a9cc20f86 | |||
| 54a34ef5ee | |||
| f097d7761d | |||
| 03717113f4 | |||
| 284a4c5520 |
Regular → Executable
Regular → Executable
@@ -1,42 +0,0 @@
|
||||
# These environment variables will be used by default if you do not create any
|
||||
# yourself in .env. This file should be safe to check into your version control
|
||||
# system. Any custom values should go in .env and .env should *not* be checked
|
||||
# into version control.
|
||||
|
||||
# location of the test database for api service scenarios (defaults to ./.redwood/test.db if not set)
|
||||
# TEST_DATABASE_URL=file:./.redwood/test.db
|
||||
|
||||
# disables Prisma CLI update notifier
|
||||
PRISMA_HIDE_UPDATE_MESSAGE=true
|
||||
|
||||
# Option to override the current environment's default api-side log level
|
||||
# See: https://redwoodjs.com/docs/logger for level options, defaults to "trace" otherwise.
|
||||
# Most applications want "debug" or "info" during dev, "trace" when you have issues and "warn" in production.
|
||||
# Ordered by how verbose they are: trace | debug | info | warn | error | silent
|
||||
# LOG_LEVEL=debug
|
||||
|
||||
FIRST_NAME=firstname
|
||||
LAST_NAME=lastname
|
||||
|
||||
COUNTRY=US
|
||||
|
||||
SMTP_HOST=smtp.example.com
|
||||
SMTP_PORT=465
|
||||
SMTP_SECURE=true
|
||||
SMTP_USER=noreply@example.com
|
||||
EMAIL_FROM=noreply@example.com
|
||||
EMAIL_TO=email@example.com
|
||||
SMTP_PASSWORD=password
|
||||
|
||||
DOMAIN=example.com
|
||||
API_DOMAIN=api.example.com
|
||||
|
||||
# Must not end with "/"
|
||||
ADDRESS_PROD=https://portfolio.example.com
|
||||
ADDRESS_DEV=http://localhost:8910
|
||||
API_ADDRESS_PROD=https://api-portfolio.example.com
|
||||
API_ADDRESS_DEV=http://localhost:8911
|
||||
|
||||
MAX_HTTP_CONNECTIONS_PER_MINUTE=60
|
||||
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/rw_portfolio
|
||||
Regular → Executable
+4
@@ -6,7 +6,11 @@
|
||||
FIRST_NAME=firstname
|
||||
LAST_NAME=lastname
|
||||
|
||||
DEFAULT_THEME=light
|
||||
|
||||
COUNTRY=US
|
||||
STATE=New York
|
||||
CITY=Manhattan
|
||||
|
||||
SMTP_HOST=smtp.example.com
|
||||
SMTP_PORT=465
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+8
-3
@@ -29,7 +29,6 @@ RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \
|
||||
|
||||
COPY --chown=node:node redwood.toml .
|
||||
COPY --chown=node:node graphql.config.js .
|
||||
COPY --chown=node:node .env.defaults .env.defaults
|
||||
|
||||
# api build
|
||||
# ---------
|
||||
@@ -62,6 +61,10 @@ FROM api_build as web_build_with_prerender
|
||||
|
||||
ARG FIRST_NAME
|
||||
ARG LAST_NAME
|
||||
ARG COUNTRY
|
||||
ARG STATE
|
||||
ARG CITY
|
||||
ARG DEFAULT_THEME
|
||||
ARG API_ADDRESS_PROD
|
||||
ARG API_ADDRESS_DEV
|
||||
|
||||
@@ -74,6 +77,10 @@ FROM base as web_build
|
||||
|
||||
ARG FIRST_NAME
|
||||
ARG LAST_NAME
|
||||
ARG COUNTRY
|
||||
ARG STATE
|
||||
ARG CITY
|
||||
ARG DEFAULT_THEME
|
||||
ARG API_ADDRESS_PROD
|
||||
ARG API_ADDRESS_DEV
|
||||
|
||||
@@ -107,7 +114,6 @@ RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \
|
||||
|
||||
COPY --chown=node:node redwood.toml .
|
||||
COPY --chown=node:node graphql.config.js .
|
||||
COPY --chown=node:node .env.defaults .env.defaults
|
||||
|
||||
COPY --chown=node:node --from=api_build /home/node/app/api/dist /home/node/app/api/dist
|
||||
COPY --chown=node:node --from=api_build /home/node/app/api/db /home/node/app/api/db
|
||||
@@ -147,7 +153,6 @@ RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \
|
||||
|
||||
COPY --chown=node:node redwood.toml .
|
||||
COPY --chown=node:node graphql.config.js .
|
||||
COPY --chown=node:node .env.defaults .env.defaults
|
||||
|
||||
COPY --chown=node:node --from=web_build /home/node/app/web/dist /home/node/app/web/dist
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ services:
|
||||
portfolio:
|
||||
container_name: portfolio
|
||||
image: git.altaiar.dev/ahmed/portfolio:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- API_PROXY_TARGET=http://localhost:8911
|
||||
@@ -25,6 +26,9 @@ services:
|
||||
- FIRST_NAME=first name # Your first name
|
||||
- LAST_NAME=lastname # Your last name
|
||||
- COUNTRY=US # ISO-3166-1 alpha-2 country code, https://en.wikipedia.org/wiki/ISO_3166-1#Codes
|
||||
- STATE=New York # Optional, state or province
|
||||
- CITY=Manhattan # Optional
|
||||
- DEFAULT_THEME=light # 'light' or 'dark'
|
||||
- SMTP_HOST=smtp.example.com
|
||||
- SMTP_PORT=465
|
||||
- SMTP_SECURE=true
|
||||
@@ -41,7 +45,8 @@ services:
|
||||
- 8910:8910 # Web
|
||||
- 8911:8911 # API
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- files:/home/node/app/api/files_prod
|
||||
command: >
|
||||
@@ -51,14 +56,19 @@ services:
|
||||
yarn rw prisma db seed &&
|
||||
yarn rw serve"
|
||||
|
||||
|
||||
db:
|
||||
container_name: portfolio-db
|
||||
image: postgres:16-bookworm
|
||||
environment:
|
||||
- POSTGRES_USER=redwood
|
||||
- POSTGRES_PASSWORD=changeme
|
||||
- POSTGRES_PASSWORD=changeme # Change to a more secure password
|
||||
- POSTGRES_DB=portfolio
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d DATABASE_URL"] # Replace DATABASE_URL with the database URL from the portfolio container
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- postgres:/var/lib/postgresql/data
|
||||
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+1
-1
@@ -11,7 +11,7 @@
|
||||
"@redwoodjs/graphql-server": "8.4.0",
|
||||
"@tus/file-store": "^1.4.0",
|
||||
"@tus/server": "^1.7.0",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"countries-list": "^3.1.1",
|
||||
"graphql-scalars": "^1.23.0",
|
||||
"nodemailer": "^6.9.14"
|
||||
},
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+14
-3
@@ -9,14 +9,25 @@ import { createServer } from '@redwoodjs/api-server'
|
||||
|
||||
import { logger } from 'src/lib/logger'
|
||||
import { handleTusUpload } from 'src/lib/tus'
|
||||
;(async () => {
|
||||
const { hasFlag } = await import('country-flag-icons')
|
||||
|
||||
if (!hasFlag(process.env.COUNTRY))
|
||||
enum Theme {
|
||||
light = 'light',
|
||||
dark = 'dark',
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const { countries } = await import('countries-list')
|
||||
|
||||
if (!Object.keys(countries).includes(process.env.COUNTRY))
|
||||
throw new Error(
|
||||
'Invalid COUNTRY environment variable, please select a valid ISO-3166-1 alpha-2 country code\n See https://en.wikipedia.org/wiki/ISO_3166-1#Codes'
|
||||
)
|
||||
|
||||
if (!(process.env.DEFAULT_THEME.toLowerCase() in Theme))
|
||||
throw new Error(
|
||||
'Invalid DEFAULT_THEME environment variable, please select either light or dark'
|
||||
)
|
||||
|
||||
const server = await createServer({
|
||||
logger,
|
||||
configureApiServer: async (server) => {
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+13
-3
@@ -4,6 +4,7 @@ services:
|
||||
portfolio:
|
||||
container_name: portfolio
|
||||
image: git.altaiar.dev/ahmed/portfolio:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- API_PROXY_TARGET=http://localhost:8911
|
||||
@@ -13,6 +14,9 @@ services:
|
||||
- FIRST_NAME=first name # Your first name
|
||||
- LAST_NAME=lastname # Your last name
|
||||
- COUNTRY=US # ISO-3166-1 alpha-2 country code, https://en.wikipedia.org/wiki/ISO_3166-1#Codes
|
||||
- STATE=New York # Optional, state or province
|
||||
- CITY=Manhattan # Optional
|
||||
- DEFAULT_THEME=light # 'light' or 'dark'
|
||||
- SMTP_HOST=smtp.example.com
|
||||
- SMTP_PORT=465
|
||||
- SMTP_SECURE=true
|
||||
@@ -29,7 +33,8 @@ services:
|
||||
- 8910:8910 # Web
|
||||
- 8911:8911 # API
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- files:/home/node/app/api/files_prod
|
||||
command: >
|
||||
@@ -39,14 +44,19 @@ services:
|
||||
yarn rw prisma db seed &&
|
||||
yarn rw serve"
|
||||
|
||||
|
||||
db:
|
||||
container_name: portfolio-db
|
||||
image: postgres:16-bookworm
|
||||
environment:
|
||||
- POSTGRES_USER=redwood
|
||||
- POSTGRES_PASSWORD=changeme
|
||||
- POSTGRES_PASSWORD=changeme # Change to a more secure password
|
||||
- POSTGRES_DB=portfolio
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d DATABASE_URL"] # Replace DATABASE_URL with the database URL from the portfolio container
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- postgres:/var/lib/postgresql/data
|
||||
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+1
-1
@@ -9,7 +9,7 @@
|
||||
title = "${FIRST_NAME} ${LAST_NAME}"
|
||||
port = 8910
|
||||
apiUrl = "/api"
|
||||
includeEnvironmentVariables = ["FIRST_NAME", "LAST_NAME", "COUNTRY", "API_ADDRESS_PROD", "API_ADDRESS_DEV"]
|
||||
includeEnvironmentVariables = ["FIRST_NAME", "LAST_NAME", "COUNTRY", "STATE", "CITY", "DEFAULT_THEME", "API_ADDRESS_PROD", "API_ADDRESS_DEV"]
|
||||
[generate]
|
||||
tests = false
|
||||
stories = false
|
||||
|
||||
Regular → Executable
Regular → Executable
+1
-5
@@ -3,8 +3,6 @@ import { db } from 'api/src/lib/db'
|
||||
|
||||
import { hashPassword } from '@redwoodjs/auth-dbauth-api'
|
||||
|
||||
const MAX_TITLES = 5
|
||||
|
||||
export default async () => {
|
||||
try {
|
||||
const admin = {
|
||||
@@ -44,9 +42,7 @@ export default async () => {
|
||||
if (!titles)
|
||||
await db.titles.create({
|
||||
data: {
|
||||
titles: Array.from({ length: MAX_TITLES }).map(
|
||||
(_, i) => `a title ${i + 1}`
|
||||
),
|
||||
titles: Array.from({ length: 3 }).map((_, i) => `title ${i + 1}`),
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+2
-3
@@ -35,14 +35,13 @@
|
||||
"@uppy/react": "^4.0.1",
|
||||
"@uppy/tus": "^4.0.0",
|
||||
"@uppy/webcam": "^4.0.1",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"countries-list": "^3.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"humanize-string": "2.1.0",
|
||||
"react": "18.3.1",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-html-parser": "^2.0.2",
|
||||
"react-typed": "^2.0.12"
|
||||
"react-html-parser": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/vite": "8.4.0",
|
||||
|
||||
Regular → Executable
Regular → Executable
|
Before Width: | Height: | Size: 757 B After Width: | Height: | Size: 757 B |
Regular → Executable
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+1
-1
@@ -31,7 +31,7 @@ const Routes = () => {
|
||||
<Route path="/admin/titles" page={TitleTitlesPage} name="titles" />
|
||||
</Set>
|
||||
|
||||
<Set wrap={ScaffoldLayout} title="Resume" titleTo="adminResume">
|
||||
<Set wrap={ScaffoldLayout} title="Résumé" titleTo="adminResume">
|
||||
<Route path="/admin/resume" page={ResumeAdminResumePage} name="adminResume" />
|
||||
</Set>
|
||||
|
||||
|
||||
Regular → Executable
Regular → Executable
@@ -1,56 +0,0 @@
|
||||
import { useState, useRef, useCallback, useEffect } from 'react'
|
||||
|
||||
const SCROLL_INTERVAL_SECONDS = 3
|
||||
|
||||
interface AutoCarouselProps {
|
||||
images: string[]
|
||||
}
|
||||
|
||||
const AutoCarousel = ({ images }: AutoCarouselProps) => {
|
||||
const [activeItem, setActiveItem] = useState<number>(0)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
const scroll = useCallback(() => {
|
||||
setActiveItem((prev) => {
|
||||
if (images.length - 1 > prev) return prev + 1
|
||||
else return 0
|
||||
})
|
||||
}, [images.length])
|
||||
|
||||
const autoScroll = useCallback(
|
||||
() => setInterval(scroll, SCROLL_INTERVAL_SECONDS * 1000),
|
||||
[scroll]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const play = autoScroll()
|
||||
return () => clearInterval(play)
|
||||
}, [autoScroll])
|
||||
|
||||
useEffect(() => {
|
||||
const width = ref.current?.getBoundingClientRect().width
|
||||
ref.current?.scroll({ left: activeItem * (width || 0) })
|
||||
}, [activeItem])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="carousel carousel-center p-2 space-x-2 rounded-box"
|
||||
>
|
||||
{images.map((image, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="carousel-item w-full h-fit my-auto justify-center"
|
||||
>
|
||||
<img
|
||||
src={image}
|
||||
alt={`${i}`}
|
||||
className="object-contain rounded-xl size-fit"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AutoCarousel
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+12
-18
@@ -10,8 +10,8 @@ interface ContactCardProps {
|
||||
}
|
||||
|
||||
const ContactCard = ({ portraitUrl, socials }: ContactCardProps) => {
|
||||
const [width, setWidth] = useState()
|
||||
const [height, setHeight] = useState()
|
||||
const [width, setWidth] = useState<number>(0)
|
||||
const [height, setHeight] = useState<number>(0)
|
||||
|
||||
const observedDiv = useRef(null)
|
||||
|
||||
@@ -53,24 +53,18 @@ const ContactCard = ({ portraitUrl, socials }: ContactCardProps) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex min-h-[calc(100vh-6rem)] items-center justify-center">
|
||||
<div className="card bg-base-100 shadow-xl md:card-side">
|
||||
<figure>
|
||||
<img
|
||||
className="contact-me-image aspect-portrait object-cover"
|
||||
src={portraitUrl}
|
||||
alt={`${process.env.FIRST_NAME} ${process.env.LAST_NAME}`}
|
||||
/>
|
||||
</figure>
|
||||
<div
|
||||
className="card-body mx-auto h-fit w-fit md:mx-0"
|
||||
ref={observedDiv}
|
||||
>
|
||||
<h2 className="card-title justify-center text-3xl md:justify-start">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="card card-compact bg-base-100 shadow-xl md:card-side">
|
||||
<img
|
||||
className="contact-me-image rounded-box aspect-portrait p-2 object-cover"
|
||||
src={portraitUrl}
|
||||
alt={`${process.env.FIRST_NAME} ${process.env.LAST_NAME}`}
|
||||
/>
|
||||
<div className="card-body mx-auto w-fit md:mx-0" ref={observedDiv}>
|
||||
<h2 className="card-title justify-center text-3xl pb-2 md:justify-start">
|
||||
Contact Me
|
||||
</h2>
|
||||
<p className="p-2"></p>
|
||||
<div className="card-actions">
|
||||
<div className="card-actions rounded-btn">
|
||||
<SocialLinks socials={socials} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+5
@@ -23,13 +23,18 @@ const PDF = ({ url, form = false }: PDFProps) => {
|
||||
<iframe
|
||||
src={url}
|
||||
title="PDF"
|
||||
content="application/pdf"
|
||||
style={{
|
||||
width: 'calc(100vw - 1rem)',
|
||||
height: `calc(100vh - ${form ? '8.5rem' : '6rem'})`,
|
||||
}}
|
||||
allowFullScreen
|
||||
className="rounded-xl"
|
||||
onError={() => setError(true)}
|
||||
onLoad={() => setError(false)}
|
||||
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
|
||||
referrerPolicy="no-referrer-when-downgrade"
|
||||
loading="lazy"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
+1
-1
@@ -87,7 +87,7 @@ const AdminProject = ({ project }: Props) => {
|
||||
key={i}
|
||||
href={image}
|
||||
target="_blank"
|
||||
className="btn btn-sm btn-square"
|
||||
className={`btn btn-sm btn-square ${i === 0 && 'btn-primary'}`}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{i + 1}
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+2
-2
@@ -73,7 +73,7 @@ const Project = ({ project }: Props) => {
|
||||
<h2 className="sm:hidden font-bold text-3xl text-center">Images</h2>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-4 pt-8 justify-center h-fit">
|
||||
<div className="flex flex-wrap gap-4 pt-8 justify-center h-fit sm:p-8">
|
||||
{project.images.length > 0 &&
|
||||
project.images.map((image, i) => (
|
||||
<a
|
||||
@@ -81,7 +81,7 @@ const Project = ({ project }: Props) => {
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
key={i}
|
||||
className="rounded-xl"
|
||||
className="rounded-box"
|
||||
>
|
||||
<img src={image} alt="" className="rounded-xl" />
|
||||
</a>
|
||||
|
||||
Regular → Executable
Regular → Executable
+8
-1
@@ -242,7 +242,14 @@ const ProjectForm = (props: ProjectFormProps) => {
|
||||
<img src={fileId} alt={i.toString()} />
|
||||
</figure>
|
||||
<div className="card-body p-2 rounded-xl">
|
||||
<div className="card-actions rounded-md justify-end">
|
||||
<div
|
||||
className={`card-actions rounded-md ${i === 0 ? 'justify-between' : 'justify-end'}`}
|
||||
>
|
||||
{i === 0 && (
|
||||
<div className="btn btn-sm shadow-xl no-animation">
|
||||
Cover Image
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-square btn-sm btn-error shadow-xl"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user