switch to go backend
All checks were successful
Build and Deploy Website / build (push) Successful in 2m26s

This commit is contained in:
2026-02-12 20:58:20 -08:00
parent 70ffdbe8d5
commit d093e4709f
45 changed files with 4298 additions and 865 deletions

View File

@@ -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
View File

@@ -1,2 +1,10 @@
.vscode .vscode
.build .DS_Store
# Frontend
Frontend/node_modules
Frontend/dist
# Backend
Backend/server
Backend/public/app/

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -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
View File

@@ -0,0 +1,3 @@
module smpark.in
go 1.23

31
Backend/main.go Normal file
View 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))
}

View 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

View File

Before

Width:  |  Height:  |  Size: 544 KiB

After

Width:  |  Height:  |  Size: 544 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -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
View 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
View File

@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 100
}

23
Frontend/eslint.config.js Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

36
Frontend/package.json Normal file
View 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
View 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
View 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
View 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>,
);

View 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>
);
}

View 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>
);
}

View 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 &middot; April 2020 &ndash; 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 &middot; May 2019 &ndash; 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 &ndash; 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 &amp; 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 &mdash; President</Header>
<Box variant="small" color="text-body-secondary">
Spring 2020 &ndash; 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 &amp; 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>
);
}

View 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
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View 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
View 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
View 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

View File

@@ -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
}

View File

@@ -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")
]
)
]
)

View File

@@ -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;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

View File

@@ -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>

View File

@@ -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>&copy; 2024 Stephen Parkinson. All rights reserved.</p>
</footer>
</body>
</html>

View File

@@ -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>Childrens 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>

File diff suppressed because one or more lines are too long

View File

@@ -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)
}

View File

@@ -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")
}
}

View File

@@ -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()

View File

@@ -1,12 +0,0 @@
import XCTest
@testable import App
class AppTests: XCTestCase {
func testStub() throws {
XCTAssert(true)
}
static let allTests = [
("testStub", testStub),
]
}

View File

@@ -1,11 +0,0 @@
#if os(Linux)
import XCTest
@testable import AppTests
XCTMain([
// AppTests
testCase(AppTests.allTests),
])
#endif