10 Commits

Author SHA1 Message Date
ahmed 0283c293ef An attempt to fix the PDF iframe not loading properly when the API domain is third-partyBasic printer CRUD
Publish Docker Image / Publish Docker Image (push) Successful in 39s
2025-04-06 18:08:05 -04:00
Ahmed Al-Taiar 8d75849c55 More reliable docker-compose.yml 2024-11-10 14:48:43 -05:00
Ahmed Al-Taiar 58b44dddad Update Dockerfile
Publish Docker Image / Publish Docker Image (push) Successful in 1m51s
2024-11-08 12:07:04 -05:00
Ahmed Al-Taiar c9de531389 Remove defaults, as they override the optional values
Publish Docker Image / Publish Docker Image (push) Failing after 12s
2024-10-26 16:37:49 -04:00
Ahmed Al-Taiar d48cfe12f2 [#1] Cleaner homepage
Publish Docker Image / Publish Docker Image (push) Successful in 1m4s
2024-10-25 18:35:23 -04:00
Ahmed Al-Taiar 3a9cc20f86 [#1] Less cluttered projects page 2024-10-25 17:34:29 -04:00
Ahmed Al-Taiar 54a34ef5ee [#1] No animated text 2024-10-24 21:22:02 -04:00
Ahmed Al-Taiar f097d7761d [#1] Clearer location 2024-10-24 20:06:09 -04:00
Ahmed Al-Taiar 03717113f4 [#1] Add default theme env variable 2024-10-24 18:38:07 -04:00
Ahmed Al-Taiar 284a4c5520 [#1] Remove carousel 2024-10-24 18:14:19 -04:00
174 changed files with 266 additions and 367 deletions
Regular → Executable
View File
Regular → Executable
View File
-42
View File
@@ -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
View File
@@ -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
View File
Regular → Executable
View File
Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+8 -3
View File
@@ -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
Regular → Executable
+13 -3
View File
@@ -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
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -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
View File
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+14 -3
View File
@@ -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
View File
View File
View File
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+13 -3
View File
@@ -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
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -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
View File
Regular → Executable
+1 -5
View File
@@ -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
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+2 -3
View File
@@ -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
View File
Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 757 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -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
View File
Regular → Executable
View File
@@ -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
View File
View File
View File
View File
+12 -18
View File
@@ -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>
View File
View File
View File
Regular → Executable
+5
View File
@@ -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"
/>
)
}
View File
View File
+1 -1
View File
@@ -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}
View File
View File
View File
+2 -2
View File
@@ -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>
View File
+8 -1
View File
@@ -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