Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactoring to move to app router #96

Merged
merged 12 commits into from
May 25, 2024
5 changes: 1 addition & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
node-version: 18
cache: 'pnpm'
- run: pnpm install
- run: pnpm start-server-and-test 'turbo dev --filter=registry' 3002 'exit 0;'
- run: pnpm start-server-and-test 'turbo dev --filter=registry' 3000 'exit 0;'
working-directory: apps/registry
build:
runs-on: ubuntu-latest
Expand All @@ -55,9 +55,6 @@ jobs:
with:
node-version: 18
cache: pnpm
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- run: pnpm install
- run: pnpm turbo build
test:
Expand Down
1 change: 0 additions & 1 deletion .ruby-version

This file was deleted.

53 changes: 18 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ This is a monorepo, will be the home of the registry, the homepage, ui kit, base

All projects hosted on this domain, will be found in the /apps folder.

- [jsonresume.org](https://jsonresume.org) - the homepage, currently built in Jekyll, would like to move to a Javascript framework for easier contributions
- found in [/apps/homepage](https://github.com/jsonresume/jsonresume.org/tree/master/apps/homepage)
- [registry.jsonresume.org](https://registry.jsonresume.org) - the opt-in hosted place blah blah
- [jsonresume.org](https://jsonresume.org) - the homepage
- found in [/apps/homepage2](https://github.com/jsonresume/jsonresume.org/tree/master/apps/homepage2)
- [registry.jsonresume.org](https://registry.jsonresume.org) - the opensource free to use registry
- found in [/apps/registry](https://github.com/jsonresume/jsonresume.org/tree/master/apps/registry)

## Requirements
Expand Down Expand Up @@ -41,9 +41,15 @@ To start an individual app;
pnpm turbo dev --filter=registry
```

## App - Registry

```
pnpm dev --filter=registry
```

**Environment variables**:

These are required to run the registry. (only the github token, but it probably crashs without the rest for now)
These are optionally to run the different parts of the registry. The main behavior of rendering resumes does not need any.

```
# classic token
Expand All @@ -59,13 +65,7 @@ PINECONE_ENVIRONMENT=
OPENAI_API_KEY=
```

## App - Registry

```
pnpm dev --filter=registry
```

This will start a local server at [http://localhost:3002/thomasdavis](http://localhost:3002/thomasdavis)
This will start a local server at [http://localhost:3000/thomasdavis](http://localhost:3000/thomasdavis)

### Formats

Expand All @@ -75,32 +75,15 @@ This will start a local server at [http://localhost:3002/thomasdavis](http://loc
- Text
- Lex

### Notes

- Gonna drop Typescript, prefer less barriers to entry
- Templates cannot read from the file system when using the registry
- Base templates using react/svelte/etc
- pdf? lol

## App - Homepage

```
pnpm dev --filter=homepage
```

Kind of a mess, but it works.

It uses Jekyll, to install it

```
gem install bundler jekyll
pnpm dev --filter=homepage2
```

Then to run it
## Contributors

```
jekyll serve
```
|Contribs|

## AI

Expand All @@ -112,27 +95,27 @@ This project creates embeddings out of Hacker News Who Is Hiring post, it then g

It is not setup to be automated at the moment, and the formatting is garbage. Each post should be sent to GPT to reformat it into a templated job description before generating embeddings.

[http://localhost:3002/thomasdavis/jobs](http://localhost:3002/thomasdavis/jobs)
[http://localhost:3000/thomasdavis/jobs](http://localhost:3000/thomasdavis/jobs)

### Letter

This is a very simple service that prompts GPT with your resume and asks to generate a cover letter.

It could be easily improved to also contain the context of the job you are applying for.

[http://localhost:3002/thomasdavis/letter](http://localhost:3002/thomasdavis/letter)
[http://localhost:3000/thomasdavis/letter](http://localhost:3000/thomasdavis/letter)

### Suggestions

This is a very simple service that prompts GPT with your resume and asks to generate a list of suggestions for you to improve your resume.

[http://localhost:3002/thomasdavis/suggestions](http://localhost:3002/thomasdavis/suggestions)
[http://localhost:3000/thomasdavis/suggestions](http://localhost:3000/thomasdavis/suggestions)

### Interview

This is an implementation of a chat bot, your resume is injected, and the conversation is included in the prompt. So you can interview your self or talk as if you were being interviewed.

[http://localhost:3002/thomasdavis/interview](http://localhost:3002/thomasdavis/interview)
[http://localhost:3000/thomasdavis/interview](http://localhost:3000/thomasdavis/interview)

# todo

Expand Down
43 changes: 43 additions & 0 deletions apps/registry/lib/error/buildError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export const ERROR_CODES = Object.freeze({
INVALID_USERNAME: 'INVALID_USERNAME',
NON_EXISTENT_GIST: 'NON_EXISTENT_GIST',
GIST_UNKNOWN_ERROR: 'GIST_UNKNOWN_ERROR',
RESUME_SCHEMA_ERROR: 'RESUME_SCHEMA_ERROR',
TEMPLATE_MISSING: 'TEMPLATE_MISSING',
UNKNOWN_TEMPLATE_ERROR: 'UNKNOWN_TEMPLATE_ERROR',
INVALID_EXTENSION: 'INVALID_EXTENSION',
UNKNOWN_FORMATTER: 'UNKNOWN_FORMATTER',
RESUME_NOT_VALID_JSON: 'RESUME_NOT_VALID_JSON',
});

export const ERROR_CODE_MESSAGES = Object.freeze({
INVALID_USERNAME: 'This is not a valid Github username',
NON_EXISTENT_GIST:
'You have no gists named resume.json or your gist is private',
GIST_UNKNOWN_ERROR: 'Cannot fetch gist, no idea why',
RESUME_SCHEMA_ERROR:
'Your resume does not conform to the schema, visit https://jsonresume.org/schema/ to double check why. But the error message below should contain all the information you need.',
TEMPLATE_MISSING:
'This theme is currently unsupported. Please visit this Github issue to request it https://github.com/jsonresume/jsonresume.org/issues/36 (unfortunately we have recently (11/2023) disabled a bunch of legacy themes due to critical flaws in them, please request if you would like them back.)',
UNKNOWN_TEMPLATE_ERROR:
'Cannot format resume, no idea why #likely-a-validation-error',
INVALID_EXTENSION: `We only support the following extensions: ${[...[]].join(
', '
)}`,
UNKNOWN_FORMATTER:
'This is a valid extension but we do not have a formatter for it. Please visit github issues.',
RESUME_NOT_VALID_JSON:
'Your resume is not valid JSON. Find an online JSON validator to help you debug this.',
});

const buildError = (error, extra) => {
return {
error: {
code: error,
message: ERROR_CODE_MESSAGES[error],
extra,
},
};
};

export default buildError;
17 changes: 17 additions & 0 deletions apps/registry/lib/formatters/formatters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import qr from './qr';
import template from './template';
import txt from './text';
import tex from './tex';
import json from './json';
import yaml from './yaml';

const formatters = {
qr,
json,
tex,
txt,
template,
yaml,
};

export default formatters;
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export const THEMES = {
rickosborne: require('jsonresume-theme-rickosborne'),
spartan: require('jsonresume-theme-spartan'),
spartacus,
stackoverflowed: require('jsonresume-theme-stackoverflowed'),
standard,
stackoverflow,
'standard-resume': require('jsonresume-theme-standard-resume'),
Expand Down
86 changes: 86 additions & 0 deletions apps/registry/lib/generateResume.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import schema from './schema';
import buildError, { ERROR_CODES } from './error/buildError';
import getResumeGist from './getResumeGist';
import formatters from './formatters/formatters';

const Validator = require('jsonschema').Validator;

const { createClient } = require('@supabase/supabase-js');

const supabaseUrl = 'https://itxuhvvwryeuzuyihpkp.supabase.co';
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

const EXTENSIONS = new Set(['qr', 'json', 'tex', 'txt', 'template', 'yaml']);

const generateResume = async (username, extension = 'template', query = {}) => {
const { theme } = query;
const formatter = formatters[extension];

if (!EXTENSIONS.has(extension)) {
return buildError(ERROR_CODES.INVALID_EXTENSION);
}

if (!formatter) {
return buildError(ERROR_CODES.UNKNOWN_FORMATTER);
}

// retrieve the users github gist
const { error: gistError, resume } = await getResumeGist(username);

if (gistError) {
return buildError(gistError);
}

const v = new Validator();
const validation = v.validate(resume, schema);

if (!validation.valid) {
return buildError(ERROR_CODES.RESUME_SCHEMA_ERROR, {
validation: validation.errors,
});
}

let selectedTheme = theme || resume.meta?.theme || 'elegant';

selectedTheme = selectedTheme.toLowerCase();

// @todo - using as a resume cache for extra features
(async () => {
try {
await supabase
.from('resumes')
.upsert(
{
username,
resume: JSON.stringify(resume),
updated_at: new Date(),
},
{ onConflict: 'username' }
)
.select();
} catch (error) {
console.error('Failed to cache resume:', error);
}
})();

const options = { ...query, theme: selectedTheme, username };

let formatted = {};

try {
formatted = await formatter.format(resume, options);
} catch (e) {
console.error(e);
// @todo - do this better
if (e.message === 'theme-missing') {
return buildError(ERROR_CODES.TEMPLATE_MISSING);
}

return buildError(ERROR_CODES.UNKNOWN_TEMPLATE_ERROR);
}

return { content: formatted.content, headers: formatted.headers || [] };
};

export default generateResume;
54 changes: 54 additions & 0 deletions apps/registry/lib/getResumeGist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import axios from 'axios';
import { find } from 'lodash';
import buildError, { ERROR_CODES } from './error/buildError';

const GITHUB_TOKEN = process.env.GITHUB_TOKEN;

const getResumeGist = async (username) => {
let gistData = null;
try {
gistData = await axios.get(
`https://api.github.com/users/${username}/gists?per_page=100`,
{
headers: {
...(GITHUB_TOKEN ? { Authorization: 'Bearer ' + GITHUB_TOKEN } : {}), // If we have no token and are in development, we can still make some requests.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use template literals for string concatenation.

-          ...(GITHUB_TOKEN ? { Authorization: 'Bearer ' + GITHUB_TOKEN } : {}),
+          ...(GITHUB_TOKEN ? { Authorization: `Bearer ${GITHUB_TOKEN}` } : {}),

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
...(GITHUB_TOKEN ? { Authorization: 'Bearer ' + GITHUB_TOKEN } : {}), // If we have no token and are in development, we can still make some requests.
...(GITHUB_TOKEN ? { Authorization: `Bearer ${GITHUB_TOKEN}` } : {}), // If we have no token and are in development, we can still make some requests.

},
}
);
} catch (e) {
console.log(e);
return buildError(ERROR_CODES.INVALID_USERNAME);
}

if (!gistData.data) {
return buildError(ERROR_CODES.GIST_UNKNOWN_ERROR);
}

const resumeUrl = find(gistData.data, (f) => {
return f.files['resume.json'];
});

if (!resumeUrl) {
return buildError(ERROR_CODES.NON_EXISTENT_GIST);
}

const gistId = resumeUrl.id;

let resumeRes = {};

try {
const fullResumeGistUrl = `https://gist.githubusercontent.com/${username}/${gistId}/raw?cachebust=${new Date().getTime()}`;

resumeRes = await axios({
method: 'GET',
headers: { 'content-type': 'application/json' },
url: fullResumeGistUrl,
});
} catch (e) {
return buildError(ERROR_CODES.GIST_UNKNOWN_ERROR);
}

return { resume: resumeRes.data };
};

export default getResumeGist;
Loading
Loading