switch to go backend
All checks were successful
Build and Deploy Website / build (push) Successful in 2m26s
@@ -5,11 +5,8 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- 'Sources/**'
|
- 'Backend/**'
|
||||||
- 'Public/**'
|
- 'Frontend/**'
|
||||||
- 'Resources/**'
|
|
||||||
- 'Package.swift'
|
|
||||||
- 'Package.resolved'
|
|
||||||
- 'Dockerfile'
|
- 'Dockerfile'
|
||||||
- '.gitea/workflows/build-and-deploy.yml'
|
- '.gitea/workflows/build-and-deploy.yml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|||||||
10
.gitignore
vendored
@@ -1,2 +1,10 @@
|
|||||||
.vscode
|
.vscode
|
||||||
.build
|
.DS_Store
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
Frontend/node_modules
|
||||||
|
Frontend/dist
|
||||||
|
|
||||||
|
# Backend
|
||||||
|
Backend/server
|
||||||
|
Backend/public/app/
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>SchemeUserState</key>
|
|
||||||
<dict>
|
|
||||||
<key>smparkin.xcscheme_^#shared#^_</key>
|
|
||||||
<dict>
|
|
||||||
<key>orderHint</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>SuppressBuildableAutocreation</key>
|
|
||||||
<dict>
|
|
||||||
<key>AppTests</key>
|
|
||||||
<dict>
|
|
||||||
<key>primary</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>Run</key>
|
|
||||||
<dict>
|
|
||||||
<key>primary</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
3
Backend/go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module smpark.in
|
||||||
|
|
||||||
|
go 1.23
|
||||||
31
Backend/main.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
publicDir := "./public"
|
||||||
|
spaIndex := filepath.Join(publicDir, "app", "index.html")
|
||||||
|
|
||||||
|
fs := http.FileServer(http.Dir(publicDir))
|
||||||
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Try to serve the static file
|
||||||
|
path := filepath.Join(publicDir, filepath.Clean(r.URL.Path))
|
||||||
|
if info, err := os.Stat(path); err == nil && !info.IsDir() {
|
||||||
|
fs.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to SPA index for client-side routing
|
||||||
|
http.ServeFile(w, r, spaIndex)
|
||||||
|
})
|
||||||
|
|
||||||
|
addr := ":8080"
|
||||||
|
log.Printf("Listening on %s", addr)
|
||||||
|
log.Fatal(http.ListenAndServe(addr, nil))
|
||||||
|
}
|
||||||
4
Backend/public/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect width="100" height="100" rx="16" fill="#9370db"/>
|
||||||
|
<text x="50" y="68" font-family="system-ui, sans-serif" font-size="48" font-weight="bold" fill="white" text-anchor="middle">SP</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 264 B |
|
Before Width: | Height: | Size: 544 KiB After Width: | Height: | Size: 544 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
90
Dockerfile
@@ -1,77 +1,41 @@
|
|||||||
# ================================
|
# ================================
|
||||||
# Build image
|
# Frontend build
|
||||||
# ================================
|
# ================================
|
||||||
FROM swift:6.1.0-noble AS build
|
FROM node:22-slim AS frontend
|
||||||
|
|
||||||
# Install OS updates and, if needed, sqlite3
|
WORKDIR /frontend
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
|
COPY Frontend/package.json Frontend/package-lock.json ./
|
||||||
&& apt-get -q update \
|
RUN npm ci
|
||||||
&& apt-get -q dist-upgrade -y\
|
COPY Frontend/ ./
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
RUN npm run build -- --outDir dist --emptyOutDir
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# Backend build
|
||||||
|
# ================================
|
||||||
|
FROM golang:1.23-alpine AS backend
|
||||||
|
|
||||||
# Set up a build area
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
COPY Backend/go.mod ./
|
||||||
# First just resolve dependencies.
|
COPY Backend/main.go ./
|
||||||
# This creates a cached layer that can be reused
|
RUN CGO_ENABLED=0 go build -o server .
|
||||||
# as long as your Package.swift/Package.resolved
|
|
||||||
# files do not change.
|
|
||||||
COPY ./Package.* ./
|
|
||||||
RUN swift package resolve
|
|
||||||
|
|
||||||
# Copy entire repo into container
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build everything, with optimizations
|
|
||||||
RUN swift build -c release --static-swift-stdlib
|
|
||||||
|
|
||||||
# Switch to the staging area
|
|
||||||
WORKDIR /staging
|
|
||||||
|
|
||||||
# Copy main executable to staging area
|
|
||||||
RUN cp "$(swift build --package-path /build -c release --show-bin-path)/Run" ./
|
|
||||||
|
|
||||||
# Copy resources bundled by SPM to staging area
|
|
||||||
RUN find -L "$(swift build --package-path /build -c release --show-bin-path)/" -regex '.*\.resources$' -exec cp -Ra {} ./ \;
|
|
||||||
|
|
||||||
# Copy any resources from the public directory and views directory if the directories exist
|
|
||||||
# Ensure that by default, neither the directory nor any of its contents are writable.
|
|
||||||
RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true
|
|
||||||
RUN [ -d /build/Resources ] && { mv /build/Resources ./Resources && chmod -R a-w ./Resources; } || true
|
|
||||||
|
|
||||||
# ================================
|
# ================================
|
||||||
# Run image
|
# Runtime
|
||||||
# ================================
|
# ================================
|
||||||
FROM ubuntu:noble
|
FROM alpine:3.21
|
||||||
|
|
||||||
# Make sure all system packages are up to date, and install only essential packages.
|
RUN adduser -D -h /app app
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
|
|
||||||
&& apt-get -q update \
|
|
||||||
&& apt-get -q dist-upgrade -y \
|
|
||||||
&& apt-get -q install -y \
|
|
||||||
ca-certificates \
|
|
||||||
tzdata \
|
|
||||||
# If your app or its dependencies import FoundationNetworking, also install `libcurl4`.
|
|
||||||
# libcurl4 \
|
|
||||||
# If your app or its dependencies import FoundationXML, also install `libxml2`.
|
|
||||||
# libxml2 \
|
|
||||||
&& rm -r /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Create a vapor user and group with /app as its home directory
|
|
||||||
RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app vapor
|
|
||||||
|
|
||||||
# Switch to the new home directory
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy built executable and any staged resources from builder
|
# Copy Go binary
|
||||||
COPY --from=build --chown=vapor:vapor /staging /app
|
COPY --from=backend --chown=app:app /build/server ./
|
||||||
|
|
||||||
# Ensure all further commands run as the vapor user
|
# Copy static assets
|
||||||
USER vapor:vapor
|
COPY --chown=app:app Backend/public/ ./public/
|
||||||
|
|
||||||
# Let Docker bind to port 8080
|
# Copy built frontend
|
||||||
|
COPY --from=frontend --chown=app:app /frontend/dist ./public/app/
|
||||||
|
|
||||||
|
USER app
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["./server"]
|
||||||
# Start the Vapor service when the image is run, default to listening on 8080 in production environment
|
|
||||||
ENTRYPOINT ["./Run"]
|
|
||||||
CMD ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"]
|
|
||||||
|
|||||||
24
Frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
7
Frontend/.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
23
Frontend/eslint.config.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
js.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
reactHooks.configs.flat.recommended,
|
||||||
|
reactRefresh.configs.vite,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
16
Frontend/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Stephen Parkinson</title>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0">
|
||||||
|
<script>
|
||||||
|
document.body.style.backgroundColor = window.matchMedia("(prefers-color-scheme: dark)").matches ? "#161d26" : "#ffffff";
|
||||||
|
</script>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3740
Frontend/package-lock.json
generated
Normal file
36
Frontend/package.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "prettier --write src/ && tsc -b && vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "prettier --write src/",
|
||||||
|
"format:check": "prettier --check src/",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@cloudscape-design/components": "^3.0.1200",
|
||||||
|
"@cloudscape-design/global-styles": "^1.0.50",
|
||||||
|
"react": "^19.2.0",
|
||||||
|
"react-dom": "^19.2.0",
|
||||||
|
"react-router-dom": "^7.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.1",
|
||||||
|
"@types/node": "^24.10.1",
|
||||||
|
"@types/react": "^19.2.7",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^5.1.1",
|
||||||
|
"eslint": "^9.39.1",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.24",
|
||||||
|
"globals": "^16.5.0",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
|
"typescript": "~5.9.3",
|
||||||
|
"typescript-eslint": "^8.48.0",
|
||||||
|
"vite": "^7.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Frontend/src/App.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-image {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
48
Frontend/src/App.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { BrowserRouter, Routes, Route, useNavigate, useLocation } from "react-router-dom";
|
||||||
|
import AppLayout from "@cloudscape-design/components/app-layout";
|
||||||
|
import SideNavigation from "@cloudscape-design/components/side-navigation";
|
||||||
|
import "@cloudscape-design/global-styles/index.css";
|
||||||
|
import "./App.css";
|
||||||
|
import Home from "./pages/Home";
|
||||||
|
import Resume from "./pages/Resume";
|
||||||
|
import NotFound from "./pages/NotFound";
|
||||||
|
|
||||||
|
function AppContent() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppLayout
|
||||||
|
toolsHide
|
||||||
|
navigation={
|
||||||
|
<SideNavigation
|
||||||
|
header={{ text: "Stephen Parkinson", href: "/" }}
|
||||||
|
activeHref={location.pathname}
|
||||||
|
onFollow={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
navigate(event.detail.href);
|
||||||
|
}}
|
||||||
|
items={[
|
||||||
|
{ type: "link", text: "Home", href: "/" },
|
||||||
|
{ type: "link", text: "Resume", href: "/resume" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
content={
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="/resume" element={<Resume />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
|
</Routes>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<AppContent />
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
Frontend/src/main.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { StrictMode } from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { applyMode, Mode } from "@cloudscape-design/global-styles";
|
||||||
|
|
||||||
|
import App from "./App.tsx";
|
||||||
|
|
||||||
|
const darkQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
applyMode(darkQuery.matches ? Mode.Dark : Mode.Light);
|
||||||
|
darkQuery.addEventListener("change", (e) => {
|
||||||
|
applyMode(e.matches ? Mode.Dark : Mode.Light);
|
||||||
|
});
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
);
|
||||||
42
Frontend/src/pages/Home.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import ContentLayout from "@cloudscape-design/components/content-layout";
|
||||||
|
import Header from "@cloudscape-design/components/header";
|
||||||
|
import Container from "@cloudscape-design/components/container";
|
||||||
|
import SpaceBetween from "@cloudscape-design/components/space-between";
|
||||||
|
import Link from "@cloudscape-design/components/link";
|
||||||
|
import Box from "@cloudscape-design/components/box";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<ContentLayout
|
||||||
|
header={
|
||||||
|
<div className="page-header">
|
||||||
|
<img src="/images/me.jpg" alt="Stephen Parkinson" className="profile-image" />
|
||||||
|
<Header variant="h1" description="Systems Development Engineer">
|
||||||
|
Stephen Parkinson
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SpaceBetween size="l">
|
||||||
|
<Container header={<Header variant="h2">About Me</Header>}>
|
||||||
|
<Box variant="p">I'm doing a lot of stuff with computers right now.</Box>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container>
|
||||||
|
<img src="/images/trees.jpg" alt="Trees" className="hero-image" />
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container header={<Header variant="h2">Contact</Header>}>
|
||||||
|
<SpaceBetween size="s" direction="horizontal">
|
||||||
|
<Link href="https://www.linkedin.com/in/stephen-parkinson" external>
|
||||||
|
LinkedIn
|
||||||
|
</Link>
|
||||||
|
<Link href="https://github.com/smparkin" external>
|
||||||
|
GitHub
|
||||||
|
</Link>
|
||||||
|
</SpaceBetween>
|
||||||
|
</Container>
|
||||||
|
</SpaceBetween>
|
||||||
|
</ContentLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
14
Frontend/src/pages/NotFound.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import ContentLayout from "@cloudscape-design/components/content-layout";
|
||||||
|
import Header from "@cloudscape-design/components/header";
|
||||||
|
import Box from "@cloudscape-design/components/box";
|
||||||
|
import Link from "@cloudscape-design/components/link";
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
return (
|
||||||
|
<ContentLayout header={<Header variant="h1">404</Header>}>
|
||||||
|
<Box variant="p">
|
||||||
|
The page you're looking for doesn't exist. <Link href="/">Go home</Link>
|
||||||
|
</Box>
|
||||||
|
</ContentLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
137
Frontend/src/pages/Resume.tsx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import ContentLayout from "@cloudscape-design/components/content-layout";
|
||||||
|
import Header from "@cloudscape-design/components/header";
|
||||||
|
import Container from "@cloudscape-design/components/container";
|
||||||
|
import SpaceBetween from "@cloudscape-design/components/space-between";
|
||||||
|
import Box from "@cloudscape-design/components/box";
|
||||||
|
import ColumnLayout from "@cloudscape-design/components/column-layout";
|
||||||
|
|
||||||
|
export default function Resume() {
|
||||||
|
return (
|
||||||
|
<ContentLayout header={<Header variant="h1">Resume</Header>}>
|
||||||
|
<SpaceBetween size="l">
|
||||||
|
<Container header={<Header variant="h2">Employment</Header>}>
|
||||||
|
<SpaceBetween size="l">
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Header variant="h3">Systems Development Engineer, Amazon Security</Header>
|
||||||
|
<Box variant="small" color="text-body-secondary">
|
||||||
|
San Luis Obispo, CA · April 2020 – Present (Promoted to full-time July
|
||||||
|
2022)
|
||||||
|
</Box>
|
||||||
|
<Box variant="p">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Create and deploy serverless applications to collect and enrich data for
|
||||||
|
analysis.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Add monitoring and alarming to existing services to allow for easier diagnosing
|
||||||
|
of potential issues.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Work with customers to understand requirements and deliver results quickly.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Header variant="h3">Student Assistant, On-Site Support Cal Poly ITS</Header>
|
||||||
|
<Box variant="small" color="text-body-secondary">
|
||||||
|
San Luis Obispo, CA · May 2019 – February 2020
|
||||||
|
</Box>
|
||||||
|
<Box variant="p">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Responsible for support of computer services and equipment across multiple
|
||||||
|
departments.
|
||||||
|
</li>
|
||||||
|
<li>Troubleshooting, detecting, and solving of technical problems.</li>
|
||||||
|
<li>
|
||||||
|
Managing macOS and Windows based computers using Active Directory, SCCM, and
|
||||||
|
Jamf.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
</SpaceBetween>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container header={<Header variant="h2">Education</Header>}>
|
||||||
|
<SpaceBetween size="l">
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Header variant="h3">California Polytechnic State University</Header>
|
||||||
|
<Box variant="small" color="text-body-secondary">
|
||||||
|
2017 – 2022
|
||||||
|
</Box>
|
||||||
|
<Box variant="p">
|
||||||
|
<strong>Software Engineering</strong>
|
||||||
|
<br />
|
||||||
|
Relevant Coursework: Introduction to Computing, Fundamentals of Computer Science,
|
||||||
|
Data Structures, Project-Based Object-Oriented Programming & Design,
|
||||||
|
Introduction to Computer Organization, Systems Programming, and Introduction to
|
||||||
|
Operating Systems.
|
||||||
|
</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Header variant="h3">Cal Poly Security Education Club — President</Header>
|
||||||
|
<Box variant="small" color="text-body-secondary">
|
||||||
|
Spring 2020 – Spring 2021
|
||||||
|
</Box>
|
||||||
|
<Box variant="p">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Responsible for planning and logistics of events including iFixit Triathlon and
|
||||||
|
Security Career Fair.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Manage a team of student officers to introduce students to security topics at
|
||||||
|
all skill levels.
|
||||||
|
</li>
|
||||||
|
<li>Coordinate with companies for presentations and special events.</li>
|
||||||
|
<li>
|
||||||
|
Presented technical talks such as "iOS Security and Jailbreaking", "machswap, a
|
||||||
|
vulnerability in XNU IPC", "Intro to SSH", "Nintendo Switch Security", "macOS
|
||||||
|
Security", and "Mac File Systems and APFS Security".
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
</SpaceBetween>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container header={<Header variant="h2">Skills</Header>}>
|
||||||
|
<ColumnLayout columns={3} variant="text-grid">
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Box variant="h3">Languages & Tools</Box>
|
||||||
|
<Box variant="p">
|
||||||
|
Python, TypeScript, React, JavaScript, C, Swift, git, zsh, Burp Suite
|
||||||
|
</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Box variant="h3">AWS</Box>
|
||||||
|
<Box variant="p">Lambda, S3, DynamoDB, CloudWatch, KMS, SSM, SES, SQS, SNS, CDK</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
<SpaceBetween size="xxs">
|
||||||
|
<Box variant="h3">Operating Systems</Box>
|
||||||
|
<Box variant="p">macOS, Windows, Linux</Box>
|
||||||
|
</SpaceBetween>
|
||||||
|
</ColumnLayout>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container header={<Header variant="h2">Projects</Header>}>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Developed a watchOS app to show the status of the White Hat lab on an Apple Watch.
|
||||||
|
</li>
|
||||||
|
<li>Created a Python script to control Spotify's API from the command line.</li>
|
||||||
|
<li>
|
||||||
|
Upgraded White Hat's network infrastructure to take advantage of the 1Gbps connection
|
||||||
|
to the internet.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Container>
|
||||||
|
</SpaceBetween>
|
||||||
|
</ContentLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
28
Frontend/tsconfig.app.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
7
Frontend/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
26
Frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2023",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"types": ["node"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
20
Frontend/vite.config.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
base: '/app/',
|
||||||
|
build: {
|
||||||
|
outDir: '../Backend/public/app',
|
||||||
|
emptyOutDir: true,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
vendor: ['react', 'react-dom'],
|
||||||
|
cloudscape: ['@cloudscape-design/components', '@cloudscape-design/global-styles'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
18
Makefile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.PHONY: all frontend backend run dev clean
|
||||||
|
|
||||||
|
all: frontend backend
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
cd Frontend && npm run build
|
||||||
|
|
||||||
|
backend:
|
||||||
|
cd Backend && go build -o server .
|
||||||
|
|
||||||
|
run: all
|
||||||
|
cd Backend && ./server
|
||||||
|
|
||||||
|
dev:
|
||||||
|
cd Frontend && npm run dev
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf Backend/server Backend/public/app
|
||||||
249
Package.resolved
@@ -1,249 +0,0 @@
|
|||||||
{
|
|
||||||
"originHash" : "6a244abdea5ff1ff36734246a0ac5fbc6830084dfc1322e83f9683689f7bb3fc",
|
|
||||||
"pins" : [
|
|
||||||
{
|
|
||||||
"identity" : "async-http-client",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/swift-server/async-http-client.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "3b265e6a00fc5c3fdb8f91f773e506990c704337",
|
|
||||||
"version" : "1.26.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "async-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/async-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "e048c8ee94967e8d8a1c2ec0e1156d6f7fa34d31",
|
|
||||||
"version" : "1.20.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "console-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/console-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "742f624a998cba2a9e653d9b1e91ad3f3a5dff6b",
|
|
||||||
"version" : "4.15.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "leaf",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/leaf.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "d469584b9186851c5a4012d11325fb9db3207ebb",
|
|
||||||
"version" : "4.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "leaf-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/leaf-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "cf186d8f2ef33e16fd1dd78df36466c22c2e632f",
|
|
||||||
"version" : "1.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "multipart-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/multipart-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "3498e60218e6003894ff95192d756e238c01f44e",
|
|
||||||
"version" : "4.7.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "routing-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/routing-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "93f7222c8e195cbad39fafb5a0e4cc85a8def7ea",
|
|
||||||
"version" : "4.9.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-algorithms",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-algorithms.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023",
|
|
||||||
"version" : "1.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-asn1",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-asn1.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "a54383ada6cecde007d374f58f864e29370ba5c3",
|
|
||||||
"version" : "1.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-atomics",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-atomics.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
|
|
||||||
"version" : "1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-collections",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-collections.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
|
|
||||||
"version" : "1.1.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-crypto",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-crypto.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "e8d6eba1fef23ae5b359c46b03f7d94be2f41fed",
|
|
||||||
"version" : "3.12.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-distributed-tracing",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-distributed-tracing.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "a64a0abc2530f767af15dd88dda7f64d5f1ff9de",
|
|
||||||
"version" : "1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-http-structured-headers",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-http-structured-headers.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "f280fc7676b9940ff2c6598642751ea333c6544f",
|
|
||||||
"version" : "1.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-http-types",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-http-types.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "a0a57e949a8903563aba4615869310c0ebf14c03",
|
|
||||||
"version" : "1.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-log",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-log.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "3d8596ed08bd13520157f0355e35caed215ffbfa",
|
|
||||||
"version" : "1.6.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-metrics",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-metrics.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "4c83e1cdf4ba538ef6e43a9bbd0bcc33a0ca46e3",
|
|
||||||
"version" : "2.7.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "34d486b01cd891297ac615e40d5999536a1e138d",
|
|
||||||
"version" : "2.83.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio-extras",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio-extras.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "f1f6f772198bee35d99dd145f1513d8581a54f2c",
|
|
||||||
"version" : "1.26.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio-http2",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio-http2.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "5e9e99ec96c53bc2c18ddd10c1e25a3cd97c55e5",
|
|
||||||
"version" : "1.38.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio-ssl",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio-ssl.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "4b38f35946d00d8f6176fe58f96d83aba64b36c7",
|
|
||||||
"version" : "2.31.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-nio-transport-services",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-nio-transport-services.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "cd1e89816d345d2523b11c55654570acd5cd4c56",
|
|
||||||
"version" : "1.24.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-numerics",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-numerics.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8",
|
|
||||||
"version" : "1.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-service-context",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-service-context.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "8946c930cae601452149e45d31d8ddfac973c3c7",
|
|
||||||
"version" : "1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift-system",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/apple/swift-system.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "a34201439c74b53f0fd71ef11741af7e7caf01e1",
|
|
||||||
"version" : "1.4.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "vapor",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/vapor.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "87b0edd2633c35de543cb7573efe5fbf456181bc",
|
|
||||||
"version" : "4.114.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "websocket-kit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/vapor/websocket-kit.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "8666c92dbbb3c8eefc8008c9c8dcf50bfd302167",
|
|
||||||
"version" : "2.16.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version" : 3
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// swift-tools-version:6.1.0
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "smparkin",
|
|
||||||
platforms: [
|
|
||||||
.macOS(.v13)
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
.package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.114.0")),
|
|
||||||
.package(url: "https://github.com/vapor/leaf.git", .upToNextMajor(from: "4.5.0")),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "App",
|
|
||||||
dependencies: [
|
|
||||||
.product(name: "Vapor", package: "vapor"),
|
|
||||||
.product(name: "Leaf", package: "leaf")
|
|
||||||
],
|
|
||||||
swiftSettings: [
|
|
||||||
.unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
|
|
||||||
]
|
|
||||||
),
|
|
||||||
.executableTarget(
|
|
||||||
name: "Run",
|
|
||||||
dependencies: [
|
|
||||||
.target(name: "App")
|
|
||||||
]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "AppTests",
|
|
||||||
dependencies: [
|
|
||||||
.target(name: "App"),
|
|
||||||
.product(name: "XCTVapor", package: "vapor")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
* {
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notfound {
|
|
||||||
position: relative;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notfound .notfound {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
-webkit-transform: translate(-50%, -50%);
|
|
||||||
-ms-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notfound {
|
|
||||||
max-width: 410px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notfound .notfound-404 {
|
|
||||||
height: 280px;
|
|
||||||
position: relative;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notfound .notfound-404 h1 {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
font-size: 230px;
|
|
||||||
margin: 0px;
|
|
||||||
font-weight: 900;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
-webkit-transform: translateX(-50%);
|
|
||||||
-ms-transform: translateX(-50%);
|
|
||||||
transform: translateX(-50%);
|
|
||||||
background: url('../images/bg.jpg') no-repeat;
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.notfound h2 {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
color: #000;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notfound p {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
color: #000;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notfound a {
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: uppercase;
|
|
||||||
background: #0046d5;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 15px 30px;
|
|
||||||
border-radius: 40px;
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 700;
|
|
||||||
-webkit-box-shadow: 0px 4px 15px -5px #0046d5;
|
|
||||||
box-shadow: 0px 4px 15px -5px #0046d5;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media only screen and (max-width: 767px) {
|
|
||||||
.notfound .notfound-404 {
|
|
||||||
height: 142px;
|
|
||||||
}
|
|
||||||
.notfound .notfound-404 h1 {
|
|
||||||
font-size: 112px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 466 KiB |
@@ -1,42 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
|
||||||
|
|
||||||
<title>404</title>
|
|
||||||
|
|
||||||
<!-- Google font -->
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,900" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- Custom stlylesheet -->
|
|
||||||
<link type="text/css" rel="stylesheet" href="/css/404style.css" />
|
|
||||||
|
|
||||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
|
||||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="notfound">
|
|
||||||
<div class="notfound">
|
|
||||||
<div class="notfound-404">
|
|
||||||
<h1>Oops!</h1>
|
|
||||||
</div>
|
|
||||||
<h2>404 - Page not found</h2>
|
|
||||||
<p>The page you are looking for might have been removed had its name changed or is temporarily unavailable.</p>
|
|
||||||
<a href="https://smpark.in">Home</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body><!-- This templates was made by Colorlib (https://colorlib.com) -->
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Stephen Parkinson</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background-color: #121212;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
header {
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 2px solid #9370db !important;
|
|
||||||
}
|
|
||||||
header h1 {
|
|
||||||
margin: 0;
|
|
||||||
color: #9370db !important;
|
|
||||||
}
|
|
||||||
header img {
|
|
||||||
max-width: 150px;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid #9370db !important;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
a:link,
|
|
||||||
a:visited {
|
|
||||||
color: #9370db !important;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: background-color 0.3s, color 0.3s;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
background-color: #9370db !important;
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
a:active {
|
|
||||||
background-color: #9370db !important;
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
section {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 40px auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
section img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto 20px auto;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 2px solid #9370db !important;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
border-top: 2px solid #9370db !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<img
|
|
||||||
src="/images/me.jpg"
|
|
||||||
alt="Stephen Parkinson"
|
|
||||||
/>
|
|
||||||
<h1>Stephen Parkinson</h1>
|
|
||||||
<p>Systems Development Engineer</p>
|
|
||||||
</header>
|
|
||||||
<section id="about">
|
|
||||||
<h2>About Me</h2>
|
|
||||||
<p>I'm doing a lot of stuff with computers right now.</p>
|
|
||||||
</section>
|
|
||||||
<section id="image">
|
|
||||||
<img
|
|
||||||
src="images/trees.jpg"
|
|
||||||
alt="Project Image"
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
<section id="contact">
|
|
||||||
<h2>Contact</h2>
|
|
||||||
<p>
|
|
||||||
Connect with me on
|
|
||||||
<a
|
|
||||||
href="https://www.linkedin.com/in/stephen-parkinson"
|
|
||||||
target="_blank"
|
|
||||||
>LinkedIn</a
|
|
||||||
>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Check out my work on
|
|
||||||
<a href="https://github.com/smparkin" target="_blank"
|
|
||||||
>GitHub</a
|
|
||||||
>.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<footer>
|
|
||||||
<p>© 2024 Stephen Parkinson. All rights reserved.</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf-8'>
|
|
||||||
<meta name='viewport' content='width=device-width'>
|
|
||||||
<title>Privacy Policy</title>
|
|
||||||
<style> body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; padding:1em; } </style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<strong>Privacy Policy</strong>
|
|
||||||
<p>
|
|
||||||
Stephen Parkinson built the Monitorr app as
|
|
||||||
a Free app. This SERVICE is provided by
|
|
||||||
Stephen Parkinson at no cost and is intended for use as
|
|
||||||
is.
|
|
||||||
</p> <p>
|
|
||||||
This page is used to inform visitors regarding my
|
|
||||||
policies with the collection, use, and disclosure of Personal
|
|
||||||
Information if anyone decided to use my Service.
|
|
||||||
</p> <p>
|
|
||||||
If you choose to use my Service, then you agree to
|
|
||||||
the collection and use of information in relation to this
|
|
||||||
policy. The Personal Information that I collect is
|
|
||||||
used for providing and improving the Service. I will not use or share your information with
|
|
||||||
anyone except as described in this Privacy Policy.
|
|
||||||
</p> <p>
|
|
||||||
The terms used in this Privacy Policy have the same meanings
|
|
||||||
as in our Terms and Conditions, which are accessible at
|
|
||||||
Monitorr unless otherwise defined in this Privacy Policy.
|
|
||||||
</p> <p><strong>Information Collection and Use</strong></p> <p>
|
|
||||||
For a better experience, while using our Service, I
|
|
||||||
may require you to provide us with certain personally
|
|
||||||
identifiable information. The information that
|
|
||||||
I request will be retained on your device and is not collected by me in any way.
|
|
||||||
</p> <!----> <p><strong>Log Data</strong></p> <p>
|
|
||||||
I want to inform you that whenever you
|
|
||||||
use my Service, in a case of an error in the app
|
|
||||||
I collect data and information (through third-party
|
|
||||||
products) on your phone called Log Data. This Log Data may
|
|
||||||
include information such as your device Internet Protocol
|
|
||||||
(“IP”) address, device name, operating system version, the
|
|
||||||
configuration of the app when utilizing my Service,
|
|
||||||
the time and date of your use of the Service, and other
|
|
||||||
statistics.
|
|
||||||
</p> <p><strong>Cookies</strong></p> <p>
|
|
||||||
Cookies are files with a small amount of data that are
|
|
||||||
commonly used as anonymous unique identifiers. These are sent
|
|
||||||
to your browser from the websites that you visit and are
|
|
||||||
stored on your device's internal memory.
|
|
||||||
</p> <p>
|
|
||||||
This Service does not use these “cookies” explicitly. However,
|
|
||||||
the app may use third-party code and libraries that use
|
|
||||||
“cookies” to collect information and improve their services.
|
|
||||||
You have the option to either accept or refuse these cookies
|
|
||||||
and know when a cookie is being sent to your device. If you
|
|
||||||
choose to refuse our cookies, you may not be able to use some
|
|
||||||
portions of this Service.
|
|
||||||
</p> <p><strong>Service Providers</strong></p> <p>
|
|
||||||
I may employ third-party companies and
|
|
||||||
individuals due to the following reasons:
|
|
||||||
</p> <ul><li>To facilitate our Service;</li> <li>To provide the Service on our behalf;</li> <li>To perform Service-related services; or</li> <li>To assist us in analyzing how our Service is used.</li></ul> <p>
|
|
||||||
I want to inform users of this Service
|
|
||||||
that these third parties have access to their Personal
|
|
||||||
Information. The reason is to perform the tasks assigned to
|
|
||||||
them on our behalf. However, they are obligated not to
|
|
||||||
disclose or use the information for any other purpose.
|
|
||||||
</p> <p><strong>Security</strong></p> <p>
|
|
||||||
I value your trust in providing us your
|
|
||||||
Personal Information, thus we are striving to use commercially
|
|
||||||
acceptable means of protecting it. But remember that no method
|
|
||||||
of transmission over the internet, or method of electronic
|
|
||||||
storage is 100% secure and reliable, and I cannot
|
|
||||||
guarantee its absolute security.
|
|
||||||
</p> <p><strong>Links to Other Sites</strong></p> <p>
|
|
||||||
This Service may contain links to other sites. If you click on
|
|
||||||
a third-party link, you will be directed to that site. Note
|
|
||||||
that these external sites are not operated by me.
|
|
||||||
Therefore, I strongly advise you to review the
|
|
||||||
Privacy Policy of these websites. I have
|
|
||||||
no control over and assume no responsibility for the content,
|
|
||||||
privacy policies, or practices of any third-party sites or
|
|
||||||
services.
|
|
||||||
</p> <p><strong>Children’s Privacy</strong></p> <!----> <div><p>
|
|
||||||
I do not knowingly collect personally
|
|
||||||
identifiable information from children. I
|
|
||||||
encourage all children to never submit any personally
|
|
||||||
identifiable information through
|
|
||||||
the Application and/or Services.
|
|
||||||
I encourage parents and legal guardians to monitor
|
|
||||||
their children's Internet usage and to help enforce this Policy by instructing
|
|
||||||
their children never to provide personally identifiable information through the Application and/or Services without their permission. If you have reason to believe that a child
|
|
||||||
has provided personally identifiable information to us through the Application and/or Services,
|
|
||||||
please contact us. You must also be at least 16 years of age to consent to the processing
|
|
||||||
of your personally identifiable information in your country (in some countries we may allow your parent
|
|
||||||
or guardian to do so on your behalf).
|
|
||||||
</p></div> <p><strong>Changes to This Privacy Policy</strong></p> <p>
|
|
||||||
I may update our Privacy Policy from
|
|
||||||
time to time. Thus, you are advised to review this page
|
|
||||||
periodically for any changes. I will
|
|
||||||
notify you of any changes by posting the new Privacy Policy on
|
|
||||||
this page.
|
|
||||||
</p> <p>This policy is effective as of 2023-03-12</p> <p><strong>Contact Us</strong></p> <p>
|
|
||||||
If you have any questions or suggestions about my
|
|
||||||
Privacy Policy, do not hesitate to contact me at stephen@smpark.in.
|
|
||||||
</p> <p>This privacy policy page was created at <a href="https://privacypolicytemplate.net" target="_blank" rel="noopener noreferrer">privacypolicytemplate.net </a>and modified/generated by <a href="https://app-privacy-policy-generator.nisrulz.com/" target="_blank" rel="noopener noreferrer">App Privacy Policy Generator</a></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import Leaf
|
|
||||||
import Vapor
|
|
||||||
|
|
||||||
// Called before your application initializes.
|
|
||||||
public func configure(_ app: Application) throws {
|
|
||||||
//leaf
|
|
||||||
app.views.use(.leaf)
|
|
||||||
|
|
||||||
//Register middleware
|
|
||||||
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
|
||||||
app.middleware.use(ErrorMiddleware.default(environment: app.environment))
|
|
||||||
|
|
||||||
//routes
|
|
||||||
try routes(app)
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import Vapor
|
|
||||||
|
|
||||||
// Register your application's routes here.
|
|
||||||
public func routes(_ app: Application) throws {
|
|
||||||
app.get("") { req async throws -> View in
|
|
||||||
return try await req.view.render("home")
|
|
||||||
}
|
|
||||||
app.get("index.html") { req async throws -> View in
|
|
||||||
return try await req.view.render("home")
|
|
||||||
}
|
|
||||||
app.get("privacy") { req async throws -> View in
|
|
||||||
return try await req.view.render("privacy")
|
|
||||||
}
|
|
||||||
app.get("welcome") { req async throws -> View in
|
|
||||||
return try await req.view.render("welcome")
|
|
||||||
}
|
|
||||||
app.get("**") { req async throws -> View in
|
|
||||||
return try await req.view.render("404")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import App
|
|
||||||
import Vapor
|
|
||||||
import Logging
|
|
||||||
|
|
||||||
var env = try Environment.detect()
|
|
||||||
try LoggingSystem.bootstrap(from: &env)
|
|
||||||
let app = try await Application.make(env)
|
|
||||||
|
|
||||||
do {
|
|
||||||
try configure(app)
|
|
||||||
try await app.execute()
|
|
||||||
} catch {
|
|
||||||
app.logger.report(error: error)
|
|
||||||
try? await app.asyncShutdown()
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
try await app.asyncShutdown()
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import XCTest
|
|
||||||
@testable import App
|
|
||||||
|
|
||||||
class AppTests: XCTestCase {
|
|
||||||
func testStub() throws {
|
|
||||||
XCTAssert(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
static let allTests = [
|
|
||||||
("testStub", testStub),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#if os(Linux)
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppTests
|
|
||||||
|
|
||||||
XCTMain([
|
|
||||||
// AppTests
|
|
||||||
testCase(AppTests.allTests),
|
|
||||||
])
|
|
||||||
|
|
||||||
#endif
|
|
||||||