diff --git a/migrations/20241116085102-initialize.js b/migrations/20241116085102-initialize.js new file mode 100644 index 0000000..b9a81ed --- /dev/null +++ b/migrations/20241116085102-initialize.js @@ -0,0 +1,59 @@ +"use strict"; + +var dbm; +var type; +var seed; +var fs = require("fs"); +var path = require("path"); +var Promise; + +/** + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { + dbm = options.dbmigrate; + type = dbm.dataType; + seed = seedLink; + Promise = options.Promise; +}; + +exports.up = function (db) { + var filePath = path.join( + __dirname, + "sqls", + "20241116085102-initialize-up.sql" + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: "utf-8" }, function (err, data) { + if (err) return reject(err); + console.log("received data: " + data); + + resolve(data); + }); + }).then(function (data) { + return db.runSql(data); + }); +}; + +exports.down = function (db) { + var filePath = path.join( + __dirname, + "sqls", + "20241116085102-initialize-down.sql" + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: "utf-8" }, function (err, data) { + if (err) return reject(err); + console.log("received data: " + data); + + resolve(data); + }); + }).then(function (data) { + return db.runSql(data); + }); +}; + +exports._meta = { + version: 1, +}; diff --git a/migrations/sqls/20241116085102-initialize-down.sql b/migrations/sqls/20241116085102-initialize-down.sql new file mode 100644 index 0000000..d6627f2 --- /dev/null +++ b/migrations/sqls/20241116085102-initialize-down.sql @@ -0,0 +1,4 @@ +-- restore old schema +insert into badgehub_old.migrations select * from badgehub.migrations; +drop schema badgehub cascade; +alter schema badgehub_old rename to badgehub; diff --git a/migrations/sqls/20241116085102-initialize-up.sql b/migrations/sqls/20241116085102-initialize-up.sql new file mode 100644 index 0000000..1ffdabb --- /dev/null +++ b/migrations/sqls/20241116085102-initialize-up.sql @@ -0,0 +1,275 @@ +-- back up with old schema +alter schema badgehub rename to badgehub_old; +create schema badgehub; +create table badgehub.migrations +( + like badgehub_old.migrations including all +); + +-- create tables + +create table badges +( + slug text not null primary key, + name text, + created_at timestamptz default now(), -- creation timestamp + updated_at timestamptz default now(), -- update timestamp + deleted_at timestamptz -- soft delete timestamp (nullable) +); + +create table users +( + id text primary key, + email text unique, + admin boolean, + name text not null, + password text not null, + remember_token text, + editor text, + public boolean, + show_projects boolean, + email_verified_at timestamptz, -- email verification timestamp + created_at timestamptz default now(), -- to capture creation timestamps + updated_at timestamptz default now(), -- to track updates + deleted_at timestamptz -- soft delete timestamp (nullable) +); + +create table projects +( + created_at timestamptz not null default now(), + updated_at timestamptz not null default now(), + deleted_at timestamptz, + version_id integer, + user_id text not null, + slug text not null primary key, + git text, + allow_team_fixes boolean, + constraint projects_user_id_fk foreign key (user_id) references users (id) on delete cascade -- if a user is deleted from the database, the projects associated with that user should also be deleted +); + +create index idx_user_id on projects (user_id); -- allow searching projects by user_id efficiently + + +create table categories +( + slug text not null primary key, -- category slug + name text not null unique, -- category name + created_at timestamptz default now(), -- creation timestamp + updated_at timestamptz default now(), -- update timestamp + deleted_at timestamptz -- soft delete timestamp (nullable) +); + +-- index for faster lookups and soft delete queries +create index idx_categories_name on categories (name); +create index idx_categories_deleted_at on categories (deleted_at); + + +create table app_metadata_jsons +( + id serial primary key, -- unique identifier + category text, -- fk to categories slug + name text, -- app name + description text, -- optional description + author text, -- optional user_name name + icon text, -- optional relative path for the icon + license_file text, -- optional license file path or type name + is_library boolean, -- whether the app is a library + is_hidden boolean, -- whether the app is hidden in the launcher + semantic_version text, -- semantic version + interpreter text, -- interpreter (e.g., 'python') + main_executable text, -- main executable path + main_executable_overrides jsonb, -- overrides for the main executable + file_mappings jsonb, -- file mappings + file_mappings_overrides jsonb, -- overrides or additions for file mappings + created_at timestamptz default now(), -- record creation timestamp + updated_at timestamptz default now(), -- record update timestamp + deleted_at timestamptz, -- soft delete timestamp (nullable) + constraint app_metadata_jsons_category_fk foreign key (category) + references categories (name) on delete set default -- categorySlug relation +); + +create index idx_app_metadata_jsons_name on app_metadata_jsons (name); +create index idx_app_metadata_jsons_is_hidden on app_metadata_jsons (is_hidden); +create index idx_app_metadata_jsons_category on app_metadata_jsons (category); + + +create table versions +( + id serial primary key, -- auto-incrementing unique identifier + project_slug text not null, -- fk to projects + app_metadata_json_id integer not null, -- fk to metadata files + revision integer not null default 0, -- version revision number + semantic_version text, -- optional semantic version + zip text, -- optional zip file name or path + size_of_zip bigint, -- optional size of the zip file + git_commit_id text, -- optional git commit id + published_at timestamptz, -- required publish timestamp + download_count bigint default 0, -- download count with default value + created_at timestamptz default now(), -- track creation time + updated_at timestamptz default now(), -- track updates + deleted_at timestamptz, -- soft delete timestamp (nullable) + constraint versions_project_slug_fk foreign key (project_slug) references projects (slug) on delete cascade, + constraint versions_app_metadata_json_id_fk foreign key (app_metadata_json_id) references app_metadata_jsons (id) on delete cascade +); +alter table projects + add constraint projects_version_id_fk foreign key (version_id) references versions (id) on delete set null; + +create index idx_versions_project_slug on versions (project_slug); +create index idx_versions_published_at on versions (published_at); + +-- Copy data from old schema to new schema +INSERT INTO badgehub.badges (name, slug, deleted_at, created_at, updated_at) +SELECT name, slug, deleted_at, created_at, updated_at +FROM badgehub_old.badges; + +INSERT INTO badgehub.categories (name, slug, deleted_at, created_at, updated_at) +SELECT name, slug, deleted_at, created_at, updated_at +FROM badgehub_old.categories; + +INSERT INTO badgehub.users (id, admin, name, email, email_verified_at, password, remember_token, editor, public, + show_projects, deleted_at, created_at, updated_at) +SELECT id, + admin, + name, + email, + email_verified_at, + password, + remember_token, + editor, + public, + show_projects, + deleted_at, + created_at, + updated_at +FROM badgehub_old.users; + +with spo as (select po.slug, + po.name, + po.description, + po.project_type, + po.user_id, + po.license, + po.download_counter, + po.created_at, + po.published_at, + po.updated_at, + po.deleted_at, + c.name as category, + u.name as author + from badgehub_old.projects po + left join badgehub_old.categories c on po.category_id = c.id + left join badgehub_old.users u on po.user_id = u.id), + inserted_app_metadata as (insert + into app_metadata_jsons (category, + name, + description, + author, + license_file, + interpreter, + created_at, + updated_at, + deleted_at) + select category, + name, + description, + author, + license, + project_type, + created_at, + updated_at, + deleted_at + from spo + returning id, name) + , + inserted_project as ( + insert + into badgehub.projects (slug, + user_id, + created_at, + updated_at, + deleted_at) + select slug, + user_id, + created_at, + updated_at, + deleted_at + from spo) + +insert +into versions (project_slug, + app_metadata_json_id, + download_count, + published_at, + created_at, + updated_at, + deleted_at) +select slug, + id, + download_counter, + published_at, + created_at, + updated_at, + deleted_at +from spo + JOIN inserted_app_metadata m ON spo.name = m.name; + +update projects +set version_id = versions.id +from versions +where versions.project_slug = projects.slug + and projects.version_id is null; + +create table versioned_dependencies +( + id serial primary key, + project_slug text not null, + depends_on_project_slug text not null, + semantic_version_range text, + created_at timestamptz default now(), -- record creation timestamp + updated_at timestamptz default now(), -- record update timestamp + deleted_at timestamptz, -- soft delete timestamp (nullable) constraint versioned_dependency_depends_on_project_slug_fk foreign key (depends_on_project_slug) references projects (slug) on delete cascade, + constraint versioned_dependency_project_slug_fk foreign key (project_slug) references projects (slug) on delete cascade +); + +with old_dependencies as (select depends_on_project_id, + po.slug as project_slug, + dpo.slug as depends_on_project_slug, + project_id, + badgehub_old.dependencies.created_at, + badgehub_old.dependencies.updated_at + from badgehub_old.dependencies + left join badgehub_old.projects po on project_id = po.id + left join badgehub_old.projects dpo on depends_on_project_id = dpo.id) +insert +into badgehub.versioned_dependencies (depends_on_project_slug, project_slug, created_at, updated_at) +select depends_on_project_slug, project_slug, created_at, updated_at +from old_dependencies; + + +create table project_statuses_on_badges +( + id serial primary key, + project_slug text not null, + badge_slug text not null, + status text, + created_at timestamptz default now(), + updated_at timestamptz default now(), + deleted_at timestamptz, + constraint project_statuses_on_badges_project_slug_fk foreign key (project_slug) references projects (slug) on delete cascade, + constraint project_statuses_on_badges_badge_slug_fk foreign key (badge_slug) references badges (slug) on delete cascade +); + +with old_statuses as (select project_id, + badge_id, + b.slug as badge_slug, + p.slug as project_slug, + status, + bp.created_at, + bp.updated_at + from badgehub_old.badge_project bp + left join badgehub_old.badges b on badge_id = b.id + left join badgehub_old.projects p on project_id = p.id) +insert +into badgehub.project_statuses_on_badges (project_slug, badge_slug, status, created_at, updated_at) +select project_slug, badge_slug, status, created_at, updated_at +from old_statuses; diff --git a/mockup-data.sql b/mockup-data.sql index 8dea5f7..7d183d1 100644 --- a/mockup-data.sql +++ b/mockup-data.sql @@ -2,8 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 16.2 (Debian 16.2-1.pgdg120+2) --- Dumped by pg_dump version 16.2 (Debian 16.2-1.pgdg120+2) +-- Dumped from database version 16.4 (Debian 16.4-1.pgdg120+2) +-- Dumped by pg_dump version 16.4 (Debian 16.4-1.pgdg120+2) SET statement_timeout = 0; SET lock_timeout = 0; @@ -25,58 +25,44 @@ CREATE SCHEMA badgehub; ALTER SCHEMA badgehub OWNER TO badgehub; --- --- Name: badge_project_status; Type: TYPE; Schema: badgehub; Owner: badgehub --- - -CREATE TYPE badgehub.badge_project_status AS ENUM ( - 'working', - 'in_progress', - 'broken', - 'unknown' -); - - -ALTER TYPE badgehub.badge_project_status OWNER TO badgehub; - --- --- Name: projects_project_type; Type: TYPE; Schema: badgehub; Owner: badgehub --- - -CREATE TYPE badgehub.projects_project_type AS ENUM ( - 'python', - 'esp32', - 'ice40' -); - - -ALTER TYPE badgehub.projects_project_type OWNER TO badgehub; - SET default_tablespace = ''; SET default_table_access_method = heap; -- --- Name: badge_project; Type: TABLE; Schema: badgehub; Owner: badgehub --- - -CREATE TABLE badgehub.badge_project ( - id bigint NOT NULL, - badge_id bigint NOT NULL, - project_id bigint NOT NULL, - status badgehub.badge_project_status DEFAULT 'unknown'::badgehub.badge_project_status NOT NULL, - created_at timestamp with time zone, - updated_at timestamp with time zone +-- Name: app_metadata_jsons; Type: TABLE; Schema: badgehub; Owner: badgehub +-- + +CREATE TABLE badgehub.app_metadata_jsons ( + id integer NOT NULL, + category text, + name text, + description text, + author text, + icon text, + license_file text, + is_library boolean, + is_hidden boolean, + semantic_version text, + interpreter text, + main_executable text, + main_executable_overrides jsonb, + file_mappings jsonb, + file_mappings_overrides jsonb, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone ); -ALTER TABLE badgehub.badge_project OWNER TO badgehub; +ALTER TABLE badgehub.app_metadata_jsons OWNER TO badgehub; -- --- Name: badge_project_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub +-- Name: app_metadata_jsons_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub -- -CREATE SEQUENCE badgehub.badge_project_id_seq +CREATE SEQUENCE badgehub.app_metadata_jsons_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -84,13 +70,13 @@ CREATE SEQUENCE badgehub.badge_project_id_seq CACHE 1; -ALTER SEQUENCE badgehub.badge_project_id_seq OWNER TO badgehub; +ALTER SEQUENCE badgehub.app_metadata_jsons_id_seq OWNER TO badgehub; -- --- Name: badge_project_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub +-- Name: app_metadata_jsons_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub -- -ALTER SEQUENCE badgehub.badge_project_id_seq OWNED BY badgehub.badge_project.id; +ALTER SEQUENCE badgehub.app_metadata_jsons_id_seq OWNED BY badgehub.app_metadata_jsons.id; -- @@ -98,24 +84,50 @@ ALTER SEQUENCE badgehub.badge_project_id_seq OWNED BY badgehub.badge_project.id; -- CREATE TABLE badgehub.badges ( - id bigint NOT NULL, - name character varying(191) NOT NULL, - slug character varying(191) NOT NULL, - constraints text, - commands text, - deleted_at timestamp with time zone, - created_at timestamp with time zone, - updated_at timestamp with time zone + slug text NOT NULL, + name text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone ); ALTER TABLE badgehub.badges OWNER TO badgehub; -- --- Name: badges_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub +-- Name: categories; Type: TABLE; Schema: badgehub; Owner: badgehub +-- + +CREATE TABLE badgehub.categories ( + slug text NOT NULL, + name text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone +); + + +ALTER TABLE badgehub.categories OWNER TO badgehub; + +-- +-- Name: migrations; Type: TABLE; Schema: badgehub; Owner: badgehub +-- + +CREATE TABLE badgehub.migrations ( + id integer NOT NULL, + name character varying(255) NOT NULL, + run_on timestamp without time zone NOT NULL +); + + +ALTER TABLE badgehub.migrations OWNER TO badgehub; + +-- +-- Name: migrations_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub -- -CREATE SEQUENCE badgehub.badges_id_seq +CREATE SEQUENCE badgehub.migrations_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -123,37 +135,38 @@ CREATE SEQUENCE badgehub.badges_id_seq CACHE 1; -ALTER SEQUENCE badgehub.badges_id_seq OWNER TO badgehub; +ALTER SEQUENCE badgehub.migrations_id_seq OWNER TO badgehub; -- --- Name: badges_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub +-- Name: migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub -- -ALTER SEQUENCE badgehub.badges_id_seq OWNED BY badgehub.badges.id; +ALTER SEQUENCE badgehub.migrations_id_seq OWNED BY badgehub.migrations.id; -- --- Name: categories; Type: TABLE; Schema: badgehub; Owner: badgehub +-- Name: project_statuses_on_badges; Type: TABLE; Schema: badgehub; Owner: badgehub -- -CREATE TABLE badgehub.categories ( - id bigint NOT NULL, - name character varying(191) NOT NULL, - slug character varying(191) NOT NULL, - deleted_at timestamp with time zone, - created_at timestamp with time zone, - updated_at timestamp with time zone, - hidden boolean DEFAULT false NOT NULL +CREATE TABLE badgehub.project_statuses_on_badges ( + id integer NOT NULL, + project_slug text NOT NULL, + badge_slug text NOT NULL, + status text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone ); -ALTER TABLE badgehub.categories OWNER TO badgehub; +ALTER TABLE badgehub.project_statuses_on_badges OWNER TO badgehub; -- --- Name: categories_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub +-- Name: project_statuses_on_badges_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub -- -CREATE SEQUENCE badgehub.categories_id_seq +CREATE SEQUENCE badgehub.project_statuses_on_badges_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -161,62 +174,79 @@ CREATE SEQUENCE badgehub.categories_id_seq CACHE 1; -ALTER SEQUENCE badgehub.categories_id_seq OWNER TO badgehub; +ALTER SEQUENCE badgehub.project_statuses_on_badges_id_seq OWNER TO badgehub; -- --- Name: categories_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub +-- Name: project_statuses_on_badges_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub -- -ALTER SEQUENCE badgehub.categories_id_seq OWNED BY badgehub.categories.id; +ALTER SEQUENCE badgehub.project_statuses_on_badges_id_seq OWNED BY badgehub.project_statuses_on_badges.id; -- --- Name: dependencies; Type: TABLE; Schema: badgehub; Owner: badgehub +-- Name: projects; Type: TABLE; Schema: badgehub; Owner: badgehub -- -CREATE TABLE badgehub.dependencies ( - project_id bigint, - depends_on_project_id bigint, - created_at timestamp with time zone, - updated_at timestamp with time zone +CREATE TABLE badgehub.projects ( + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + version_id integer, + user_id text NOT NULL, + slug text NOT NULL, + git text, + allow_team_fixes boolean ); -ALTER TABLE badgehub.dependencies OWNER TO badgehub; +ALTER TABLE badgehub.projects OWNER TO badgehub; -- --- Name: projects; Type: TABLE; Schema: badgehub; Owner: badgehub +-- Name: users; Type: TABLE; Schema: badgehub; Owner: badgehub -- -CREATE TABLE badgehub.projects ( - id bigint NOT NULL, - category_id bigint DEFAULT '1'::bigint NOT NULL, - user_id bigint NOT NULL, - name character varying(191) NOT NULL, - slug character varying(191), - min_firmware bigint, - max_firmware bigint, - git character varying(191), - git_commit_id character varying(191), - published_at timestamp with time zone, - deleted_at timestamp with time zone, - created_at timestamp with time zone, - updated_at timestamp with time zone, - download_counter bigint DEFAULT '0'::bigint NOT NULL, - allow_team_fixes boolean DEFAULT true NOT NULL, - project_type badgehub.projects_project_type DEFAULT 'python'::badgehub.projects_project_type NOT NULL, - license character varying(191) DEFAULT 'MIT'::character varying NOT NULL, - description character varying(500) +CREATE TABLE badgehub.users ( + id text NOT NULL, + email text, + admin boolean, + name text NOT NULL, + password text NOT NULL, + remember_token text, + editor text, + public boolean, + show_projects boolean, + email_verified_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone ); -ALTER TABLE badgehub.projects OWNER TO badgehub; +ALTER TABLE badgehub.users OWNER TO badgehub; -- --- Name: projects_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub +-- Name: versioned_dependencies; Type: TABLE; Schema: badgehub; Owner: badgehub -- -CREATE SEQUENCE badgehub.projects_id_seq +CREATE TABLE badgehub.versioned_dependencies ( + id integer NOT NULL, + project_slug text NOT NULL, + depends_on_project_slug text NOT NULL, + semantic_version_range text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone +); + + +ALTER TABLE badgehub.versioned_dependencies OWNER TO badgehub; + +-- +-- Name: versioned_dependencies_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub +-- + +CREATE SEQUENCE badgehub.versioned_dependencies_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -224,45 +254,44 @@ CREATE SEQUENCE badgehub.projects_id_seq CACHE 1; -ALTER SEQUENCE badgehub.projects_id_seq OWNER TO badgehub; +ALTER SEQUENCE badgehub.versioned_dependencies_id_seq OWNER TO badgehub; -- --- Name: projects_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub +-- Name: versioned_dependencies_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub -- -ALTER SEQUENCE badgehub.projects_id_seq OWNED BY badgehub.projects.id; +ALTER SEQUENCE badgehub.versioned_dependencies_id_seq OWNED BY badgehub.versioned_dependencies.id; -- --- Name: users; Type: TABLE; Schema: badgehub; Owner: badgehub +-- Name: versions; Type: TABLE; Schema: badgehub; Owner: badgehub -- -CREATE TABLE badgehub.users ( - id bigint NOT NULL, - admin boolean DEFAULT false NOT NULL, - name character varying(191) NOT NULL, - email character varying(191) NOT NULL, - email_verified_at timestamp with time zone, - password character varying(191) NOT NULL, - remember_token character varying(100), - editor character varying(80) DEFAULT 'default'::character varying NOT NULL, - public boolean DEFAULT false NOT NULL, - show_projects boolean DEFAULT true NOT NULL, - google2fa_enabled boolean DEFAULT false NOT NULL, - google2fa_secret text, - deleted_at timestamp with time zone, - created_at timestamp with time zone, - updated_at timestamp with time zone +CREATE TABLE badgehub.versions ( + id integer NOT NULL, + project_slug text NOT NULL, + app_metadata_json_id integer NOT NULL, + revision integer DEFAULT 0 NOT NULL, + semantic_version text, + zip text, + size_of_zip bigint, + git_commit_id text, + published_at timestamp with time zone, + download_count bigint DEFAULT 0, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone ); -ALTER TABLE badgehub.users OWNER TO badgehub; +ALTER TABLE badgehub.versions OWNER TO badgehub; -- --- Name: users_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub +-- Name: versions_id_seq; Type: SEQUENCE; Schema: badgehub; Owner: badgehub -- -CREATE SEQUENCE badgehub.users_id_seq +CREATE SEQUENCE badgehub.versions_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -270,160 +299,142 @@ CREATE SEQUENCE badgehub.users_id_seq CACHE 1; -ALTER SEQUENCE badgehub.users_id_seq OWNER TO badgehub; +ALTER SEQUENCE badgehub.versions_id_seq OWNER TO badgehub; -- --- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub +-- Name: versions_id_seq; Type: SEQUENCE OWNED BY; Schema: badgehub; Owner: badgehub -- -ALTER SEQUENCE badgehub.users_id_seq OWNED BY badgehub.users.id; +ALTER SEQUENCE badgehub.versions_id_seq OWNED BY badgehub.versions.id; -- --- Name: badge_project id; Type: DEFAULT; Schema: badgehub; Owner: badgehub +-- Name: app_metadata_jsons id; Type: DEFAULT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.badge_project ALTER COLUMN id SET DEFAULT nextval('badgehub.badge_project_id_seq'::regclass); +ALTER TABLE ONLY badgehub.app_metadata_jsons ALTER COLUMN id SET DEFAULT nextval('badgehub.app_metadata_jsons_id_seq'::regclass); -- --- Name: badges id; Type: DEFAULT; Schema: badgehub; Owner: badgehub +-- Name: migrations id; Type: DEFAULT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.badges ALTER COLUMN id SET DEFAULT nextval('badgehub.badges_id_seq'::regclass); +ALTER TABLE ONLY badgehub.migrations ALTER COLUMN id SET DEFAULT nextval('badgehub.migrations_id_seq'::regclass); -- --- Name: categories id; Type: DEFAULT; Schema: badgehub; Owner: badgehub +-- Name: project_statuses_on_badges id; Type: DEFAULT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.categories ALTER COLUMN id SET DEFAULT nextval('badgehub.categories_id_seq'::regclass); +ALTER TABLE ONLY badgehub.project_statuses_on_badges ALTER COLUMN id SET DEFAULT nextval('badgehub.project_statuses_on_badges_id_seq'::regclass); -- --- Name: projects id; Type: DEFAULT; Schema: badgehub; Owner: badgehub +-- Name: versioned_dependencies id; Type: DEFAULT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.projects ALTER COLUMN id SET DEFAULT nextval('badgehub.projects_id_seq'::regclass); +ALTER TABLE ONLY badgehub.versioned_dependencies ALTER COLUMN id SET DEFAULT nextval('badgehub.versioned_dependencies_id_seq'::regclass); -- --- Name: users id; Type: DEFAULT; Schema: badgehub; Owner: badgehub +-- Name: versions id; Type: DEFAULT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.users ALTER COLUMN id SET DEFAULT nextval('badgehub.users_id_seq'::regclass); +ALTER TABLE ONLY badgehub.versions ALTER COLUMN id SET DEFAULT nextval('badgehub.versions_id_seq'::regclass); -- --- Data for Name: badge_project; Type: TABLE DATA; Schema: badgehub; Owner: badgehub --- - -COPY badgehub.badge_project (id, badge_id, project_id, status, created_at, updated_at) FROM stdin; -556 5 0 unknown \N \N -557 2 0 unknown \N \N -558 5 1 unknown \N \N -559 5 2 unknown \N \N -560 1 3 unknown \N \N -561 2 3 unknown \N \N -562 2 4 unknown \N \N -563 2 5 unknown \N \N -564 1 6 unknown \N \N -565 2 7 unknown \N \N -566 5 8 unknown \N \N -567 5 9 unknown \N \N -568 2 10 unknown \N \N -569 2 11 unknown \N \N -570 1 12 unknown \N \N -571 2 12 unknown \N \N -572 2 13 unknown \N \N -573 2 14 unknown \N \N -574 5 15 unknown \N \N -575 1 16 unknown \N \N -576 5 17 unknown \N \N -577 2 17 unknown \N \N -578 1 18 unknown \N \N -579 5 18 unknown \N \N -580 1 19 unknown \N \N -581 2 20 unknown \N \N -582 5 20 unknown \N \N -583 1 21 unknown \N \N -584 2 22 unknown \N \N -585 5 23 unknown \N \N -586 2 24 unknown \N \N -587 5 24 unknown \N \N -588 1 25 unknown \N \N -589 2 25 unknown \N \N -590 5 26 unknown \N \N -591 5 27 unknown \N \N -592 1 27 unknown \N \N -593 2 28 unknown \N \N -594 2 29 unknown \N \N -595 2 30 unknown \N \N -596 5 31 unknown \N \N -597 1 32 unknown \N \N -598 5 33 unknown \N \N -599 5 34 unknown \N \N -600 2 35 unknown \N \N -601 5 36 unknown \N \N -602 1 36 unknown \N \N -603 2 37 unknown \N \N -604 5 38 unknown \N \N -605 1 39 unknown \N \N -606 1 40 unknown \N \N -607 2 41 unknown \N \N -608 1 41 unknown \N \N -609 1 42 unknown \N \N -610 5 43 unknown \N \N -611 5 44 unknown \N \N -612 1 45 unknown \N \N -613 5 45 unknown \N \N -614 1 46 unknown \N \N -615 1 47 unknown \N \N -616 2 47 unknown \N \N -617 5 48 unknown \N \N -618 5 49 unknown \N \N -619 1 50 unknown \N \N -620 5 51 unknown \N \N -621 1 52 unknown \N \N -622 2 53 unknown \N \N -623 1 54 unknown \N \N -624 5 55 unknown \N \N -625 1 55 unknown \N \N -626 2 56 unknown \N \N -627 2 57 unknown \N \N -628 1 57 unknown \N \N -629 1 58 unknown \N \N -630 5 59 unknown \N \N -631 5 60 unknown \N \N -632 2 61 unknown \N \N -633 5 62 unknown \N \N -634 1 63 unknown \N \N -635 2 64 unknown \N \N -636 5 65 unknown \N \N -637 1 66 unknown \N \N -638 5 67 unknown \N \N -639 1 67 unknown \N \N -640 2 68 unknown \N \N -641 1 69 unknown \N \N -642 1 70 unknown \N \N -643 5 70 unknown \N \N -644 2 71 unknown \N \N -645 5 72 unknown \N \N -646 5 73 unknown \N \N -647 1 74 unknown \N \N -648 5 74 unknown \N \N -649 2 75 unknown \N \N -650 1 76 unknown \N \N -651 5 77 unknown \N \N -652 1 78 unknown \N \N -653 1 79 unknown \N \N -654 1 80 unknown \N \N -655 2 81 unknown \N \N -656 2 82 unknown \N \N -657 1 83 unknown \N \N -658 5 84 unknown \N \N -659 5 85 unknown \N \N -660 5 86 unknown \N \N +-- Data for Name: app_metadata_jsons; Type: TABLE DATA; Schema: badgehub; Owner: badgehub +-- + +COPY badgehub.app_metadata_jsons (id, category, name, description, author, icon, license_file, is_library, is_hidden, semantic_version, interpreter, main_executable, main_executable_overrides, file_mappings, file_mappings_overrides, created_at, updated_at, deleted_at) FROM stdin; +1 Silly CodeCraft Make some magic happen with CodeCraft. GameGlobetrotter \N MIT \N \N \N python \N \N \N \N 2024-01-05 11:29:07.05+00 2024-03-20 11:29:07.05+00 \N +2 Games PixelPulse Make some magic happen with PixelPulse. GameGizmo \N MIT \N \N \N python \N \N \N \N 2023-08-30 11:29:07.053+00 2023-10-29 11:29:07.053+00 \N +3 Event related BitBlast Make some magic happen with BitBlast. GameGuardian \N MIT \N \N \N python \N \N \N \N 2023-09-22 11:29:07.055+00 2023-11-13 11:29:07.055+00 \N +4 Event related NanoGames Make some magic happen with NanoGames. DigitalDynamo \N MIT \N \N \N python \N \N \N \N 2022-10-24 11:29:07.056+00 2023-01-02 11:29:07.057+00 \N +5 Unusable ElectraPlay With ElectraPlay, you can do interesting things with the sensors. PixelPirate \N MIT \N \N \N python \N \N \N \N 2023-09-04 11:29:07.058+00 2023-10-06 11:29:07.058+00 \N +6 SAO CircuitForge With CircuitForge, you can do interesting things with the sensors. PixelPro \N MIT \N \N \N python \N \N \N \N 2023-01-28 11:29:07.06+00 2023-04-24 11:29:07.06+00 \N +7 Event related ByteBash Use ByteBash for some cool graphical effects. CodeConqueror \N MIT \N \N \N python \N \N \N \N 2023-08-02 11:29:07.061+00 2023-10-27 11:29:07.061+00 \N +8 Adult CodeCanvas With CodeCanvas, you can do interesting things with the sensors. TechTornado \N MIT \N \N \N python \N \N \N \N 2023-12-31 11:29:07.063+00 2024-02-09 11:29:07.063+00 \N +9 Unusable SparkScript Make some magic happen with SparkScript. ElectronExplorer \N MIT \N \N \N python \N \N \N \N 2022-12-13 11:29:07.064+00 2023-01-13 11:29:07.064+00 \N +10 Data LogicLand Use LogicLand for some cool graphical effects. CodeConqueror \N MIT \N \N \N python \N \N \N \N 2023-02-05 11:29:07.066+00 2023-05-13 11:29:07.066+00 \N +11 Event related MicroArcade MicroArcade is just some silly test app. LogicLuminary \N MIT \N \N \N python \N \N \N \N 2024-01-05 11:29:07.069+00 2024-03-01 11:29:07.069+00 \N +12 SAO CodeCraze Use CodeCraze for some cool graphical effects. CodeCreator \N MIT \N \N \N python \N \N \N \N 2024-05-07 11:29:07.071+00 2024-07-22 11:29:07.071+00 \N +13 SAO GameGenius GameGenius is just some silly test app. ElectronExplorer \N MIT \N \N \N python \N \N \N \N 2023-12-14 11:29:07.073+00 2024-01-22 11:29:07.073+00 \N +14 Wearable PixelPal Make some magic happen with PixelPal. PixelPro \N MIT \N \N \N python \N \N \N \N 2023-08-10 11:29:07.075+00 2023-10-23 11:29:07.075+00 \N +15 Graphics Electronica Use Electronica for some cool graphical effects. NanoNinja \N MIT \N \N \N python \N \N \N \N 2024-02-26 11:29:07.078+00 2024-04-29 11:29:07.078+00 \N +16 Event related CodeQuest Use CodeQuest for some cool graphical effects. LogicLabyrinth \N MIT \N \N \N python \N \N \N \N 2023-05-15 11:29:07.08+00 2023-07-04 11:29:07.08+00 \N +17 Troll CircuitCraft Make some magic happen with CircuitCraft. GameGlider \N MIT \N \N \N python \N \N \N \N 2023-06-19 11:29:07.082+00 2023-07-20 11:29:07.083+00 \N +18 Troll ByteBeat ByteBeat is just some silly test app. ElectronEmperor \N MIT \N \N \N python \N \N \N \N 2023-06-07 11:29:07.084+00 2023-07-30 11:29:07.084+00 \N +19 Data NanoNexus With NanoNexus, you can do interesting things with the sensors. ByteBrawler \N MIT \N \N \N python \N \N \N \N 2023-06-28 11:29:07.087+00 2023-07-16 11:29:07.087+00 \N +20 Event related BitBox With BitBox, you can do interesting things with the sensors. CircuitChampion \N MIT \N \N \N python \N \N \N \N 2023-10-15 11:29:07.089+00 2023-10-21 11:29:07.089+00 \N +21 Unusable CircuitChaos Use CircuitChaos for some cool graphical effects. CodeConqueror \N MIT \N \N \N python \N \N \N \N 2023-02-08 11:29:07.09+00 2023-03-11 11:29:07.09+00 \N +22 Data CodeCrafter Use CodeCrafter for some cool graphical effects. CircuitCaptain \N MIT \N \N \N python \N \N \N \N 2024-04-16 11:29:07.092+00 2024-04-28 11:29:07.092+00 \N +23 Virus PixelPioneer PixelPioneer is just some silly test app. NanoNoble \N MIT \N \N \N python \N \N \N \N 2022-12-07 11:29:07.094+00 2023-02-18 11:29:07.094+00 \N +24 Unusable LogicLab Use LogicLab for some cool graphical effects. GameGazer \N MIT \N \N \N python \N \N \N \N 2023-05-14 11:29:07.096+00 2023-08-16 11:29:07.096+00 \N +25 Data ByteBlitz Make some magic happen with ByteBlitz. LogicLegend \N MIT \N \N \N python \N \N \N \N 2024-03-29 11:29:07.099+00 2024-05-23 11:29:07.099+00 \N +26 Virus CodeWave Use CodeWave for some cool graphical effects. GameGazer \N MIT \N \N \N python \N \N \N \N 2024-03-09 11:29:07.1+00 2024-06-07 11:29:07.1+00 \N +27 Event related NanoNet NanoNet is just some silly test app. ElectronExpedition \N MIT \N \N \N python \N \N \N \N 2023-11-16 11:29:07.102+00 2024-01-16 11:29:07.102+00 \N +28 Wearable ElectraForge Make some magic happen with ElectraForge. ElectronExpedition \N MIT \N \N \N python \N \N \N \N 2023-04-21 11:29:07.103+00 2023-06-29 11:29:07.104+00 \N +29 Wearable GameGrid With GameGrid, you can do interesting things with the sensors. ElectronEager \N MIT \N \N \N python \N \N \N \N 2022-10-22 11:29:07.105+00 2022-12-09 11:29:07.105+00 \N +30 Silly LogicLoom With LogicLoom, you can do interesting things with the sensors. NanoNinja \N MIT \N \N \N python \N \N \N \N 2023-08-28 11:29:07.106+00 2023-10-03 11:29:07.106+00 \N +31 Silly PixelPlaza Use PixelPlaza for some cool graphical effects. PixelPilot \N MIT \N \N \N python \N \N \N \N 2023-06-27 11:29:07.108+00 2023-06-28 11:29:07.108+00 \N +32 Event related CodeCity CodeCity is just some silly test app. GameGizmo \N MIT \N \N \N python \N \N \N \N 2022-10-01 11:29:07.11+00 2022-10-27 11:29:07.11+00 \N +33 Troll NanoArcade Use NanoArcade for some cool graphical effects. PixelPulse \N MIT \N \N \N python \N \N \N \N 2023-07-24 11:29:07.112+00 2023-08-11 11:29:07.112+00 \N +34 Adult ElectronEra With ElectronEra, you can do interesting things with the sensors. ElectronEcho \N MIT \N \N \N python \N \N \N \N 2023-11-12 11:29:07.114+00 2024-01-17 11:29:07.114+00 \N +35 Troll BitBazaar Make some magic happen with BitBazaar. GameGlider \N MIT \N \N \N python \N \N \N \N 2022-12-12 11:29:07.115+00 2023-02-13 11:29:07.115+00 \N +36 SAO LogicLegends LogicLegends is just some silly test app. ByteBandit \N MIT \N \N \N python \N \N \N \N 2023-03-16 11:29:07.116+00 2023-05-22 11:29:07.116+00 \N +37 Data CodeClan Use CodeClan for some cool graphical effects. NanoNerd \N MIT \N \N \N python \N \N \N \N 2023-11-28 11:29:07.118+00 2024-01-06 11:29:07.118+00 \N +38 Adult PixelPortal PixelPortal is just some silly test app. PixelPilot \N MIT \N \N \N python \N \N \N \N 2024-02-12 11:29:07.119+00 2024-04-25 11:29:07.119+00 \N +39 Adult CircuitCraze With CircuitCraze, you can do interesting things with the sensors. GameGlider \N MIT \N \N \N python \N \N \N \N 2022-11-04 11:29:07.12+00 2023-01-22 11:29:07.12+00 \N +40 Utility ByteBuster With ByteBuster, you can do interesting things with the sensors. CyberCraftsman \N MIT \N \N \N python \N \N \N \N 2022-12-09 11:29:07.122+00 2022-12-24 11:29:07.122+00 \N +41 Silly NanoNovel Make some magic happen with NanoNovel. PixelPulse \N MIT \N \N \N python \N \N \N \N 2024-03-17 11:29:07.123+00 2024-05-14 11:29:07.123+00 \N +42 Games ElectraEden Use ElectraEden for some cool graphical effects. CodeCrusader \N MIT \N \N \N python \N \N \N \N 2023-05-19 11:29:07.125+00 2023-06-03 11:29:07.125+00 \N +43 Adult CodeComet With CodeComet, you can do interesting things with the sensors. CircuitCaptain \N MIT \N \N \N python \N \N \N \N 2024-02-25 11:29:07.126+00 2024-03-05 11:29:07.126+00 \N +44 SAO PixelPlayground Make some magic happen with PixelPlayground. DigitalDreamer \N MIT \N \N \N python \N \N \N \N 2023-02-15 11:29:07.128+00 2023-05-12 11:29:07.128+00 \N +45 Uncategorised LogicLandia Use LogicLandia for some cool graphical effects. CodeConnoisseur \N MIT \N \N \N python \N \N \N \N 2023-08-10 11:29:07.129+00 2023-09-23 11:29:07.129+00 \N +46 Silly ByteBounce With ByteBounce, you can do interesting things with the sensors. GameGlider \N MIT \N \N \N python \N \N \N \N 2024-03-02 11:29:07.13+00 2024-05-15 11:29:07.13+00 \N +47 Games CircuitCarnival Make some magic happen with CircuitCarnival. LogicLore \N MIT \N \N \N python \N \N \N \N 2023-02-03 11:29:07.131+00 2023-03-10 11:29:07.131+00 \N +48 SAO CodeCove Use CodeCove for some cool graphical effects. ElectronEagle \N MIT \N \N \N python \N \N \N \N 2022-10-15 11:29:07.132+00 2022-12-05 11:29:07.132+00 \N +49 Games NanoNest With NanoNest, you can do interesting things with the sensors. CircuitChampion \N MIT \N \N \N python \N \N \N \N 2023-10-05 11:29:07.133+00 2023-11-12 11:29:07.134+00 \N +50 Uncategorised ElectraEntertain ElectraEntertain is just some silly test app. ElectronEagle \N MIT \N \N \N python \N \N \N \N 2023-07-11 11:29:07.135+00 2023-09-21 11:29:07.135+00 \N +51 Adult GameGalaxy Use GameGalaxy for some cool graphical effects. GameGladiator \N MIT \N \N \N python \N \N \N \N 2024-02-02 11:29:07.136+00 2024-05-05 11:29:07.136+00 \N +52 Unusable LogicLabyrinth Make some magic happen with LogicLabyrinth. LogicLion \N MIT \N \N \N python \N \N \N \N 2024-01-04 11:29:07.138+00 2024-01-04 11:29:07.138+00 \N +53 Unusable ByteBlaster ByteBlaster is just some silly test app. DigitalDreamer \N MIT \N \N \N python \N \N \N \N 2022-10-12 11:29:07.14+00 2022-11-07 11:29:07.14+00 \N +54 Data CodeCompass CodeCompass is just some silly test app. CircuitCraze \N MIT \N \N \N python \N \N \N \N 2023-02-18 11:29:07.142+00 2023-03-20 11:29:07.142+00 \N +55 Silly NanoNation Use NanoNation for some cool graphical effects. GameGuru \N MIT \N \N \N python \N \N \N \N 2024-03-06 11:29:07.143+00 2024-06-11 11:29:07.143+00 \N +56 Silly ElectraEmpire ElectraEmpire is just some silly test app. CyberCipher \N MIT \N \N \N python \N \N \N \N 2023-04-17 11:29:07.144+00 2023-06-14 11:29:07.145+00 \N +57 Adult GameGarden Make some magic happen with GameGarden. LogicLion \N MIT \N \N \N python \N \N \N \N 2024-04-04 11:29:07.146+00 2024-04-29 11:29:07.146+00 \N +58 Graphics PixelPeak With PixelPeak, you can do interesting things with the sensors. CircuitCaptain \N MIT \N \N \N python \N \N \N \N 2023-06-13 11:29:07.147+00 2023-07-13 11:29:07.147+00 \N +59 Games CircuitCelestial CircuitCelestial is just some silly test app. LogicLoreMaster \N MIT \N \N \N python \N \N \N \N 2023-07-14 11:29:07.149+00 2023-07-19 11:29:07.149+00 \N +60 Hardware CodeCrusade CodeCrusade is just some silly test app. PixelPioneer \N MIT \N \N \N python \N \N \N \N 2023-10-09 11:29:07.15+00 2023-12-23 11:29:07.15+00 \N +61 Unusable NanoNebula NanoNebula is just some silly test app. CircuitChampion \N MIT \N \N \N python \N \N \N \N 2023-03-03 11:29:07.151+00 2023-05-22 11:29:07.151+00 \N +62 Utility ElectraEnclave Use ElectraEnclave for some cool graphical effects. LogicLion \N MIT \N \N \N python \N \N \N \N 2023-09-18 11:29:07.153+00 2023-10-07 11:29:07.153+00 \N +63 Graphics GameGizmo Use GameGizmo for some cool graphical effects. GameGlider \N MIT \N \N \N python \N \N \N \N 2023-12-04 11:29:07.155+00 2024-01-17 11:29:07.155+00 \N +64 SAO PixelPlanet With PixelPlanet, you can do interesting things with the sensors. CircuitChamp \N MIT \N \N \N python \N \N \N \N 2023-02-04 11:29:07.156+00 2023-04-06 11:29:07.156+00 \N +65 Graphics LogicLounge Make some magic happen with LogicLounge. LogicLoreMaster \N MIT \N \N \N python \N \N \N \N 2022-12-24 11:29:07.157+00 2023-03-02 11:29:07.157+00 \N +66 Silly ByteBeacon Use ByteBeacon for some cool graphical effects. CyberCraft \N MIT \N \N \N python \N \N \N \N 2024-01-25 11:29:07.158+00 2024-02-04 11:29:07.158+00 \N +67 Hardware CodeCircus CodeCircus is just some silly test app. CyberSavvy \N MIT \N \N \N python \N \N \N \N 2023-07-04 11:29:07.16+00 2023-08-14 11:29:07.16+00 \N +68 Adult NanoNook Make some magic happen with NanoNook. ElectronEnigma \N MIT \N \N \N python \N \N \N \N 2022-12-14 11:29:07.161+00 2023-03-12 11:29:07.161+00 \N +69 Uncategorised ElectraElysium Make some magic happen with ElectraElysium. LogicLoreMaster \N MIT \N \N \N python \N \N \N \N 2023-06-25 11:29:07.163+00 2023-07-28 11:29:07.163+00 \N +70 Graphics GameGlimpse GameGlimpse is just some silly test app. ByteBlitz \N MIT \N \N \N python \N \N \N \N 2023-01-24 11:29:07.164+00 2023-03-12 11:29:07.164+00 \N +71 Virus PixelParadise PixelParadise is just some silly test app. CodeConqueror \N MIT \N \N \N python \N \N \N \N 2023-03-02 11:29:07.165+00 2023-03-22 11:29:07.165+00 \N +72 SAO CodeCoast With CodeCoast, you can do interesting things with the sensors. ByteBrawler \N MIT \N \N \N python \N \N \N \N 2024-01-18 11:29:07.167+00 2024-02-04 11:29:07.167+00 \N +73 Utility NanoNirvana NanoNirvana is just some silly test app. ByteBrawler \N MIT \N \N \N python \N \N \N \N 2023-06-18 11:29:07.168+00 2023-07-18 11:29:07.168+00 \N +74 Utility ElectraEdifice Make some magic happen with ElectraEdifice. LogicLoreMaster \N MIT \N \N \N python \N \N \N \N 2022-11-15 11:29:07.17+00 2022-11-25 11:29:07.17+00 \N +75 Hacking GameGen GameGen is just some silly test app. LogicLoreMaster \N MIT \N \N \N python \N \N \N \N 2023-01-16 11:29:07.171+00 2023-03-02 11:29:07.171+00 \N +76 Wearable PixelPandemonium Make some magic happen with PixelPandemonium. ByteBandit \N MIT \N \N \N python \N \N \N \N 2023-12-21 11:29:07.172+00 2024-03-23 11:29:07.172+00 \N +77 Silly LogicLagoon Use LogicLagoon for some cool graphical effects. CodeCrusader \N MIT \N \N \N python \N \N \N \N 2023-11-25 11:29:07.174+00 2023-12-13 11:29:07.174+00 \N +78 Games ByteBlaze With ByteBlaze, you can do interesting things with the sensors. LogicLion \N MIT \N \N \N python \N \N \N \N 2023-03-20 11:29:07.175+00 2023-04-05 11:29:07.175+00 \N +79 Silly CodeCorridor CodeCorridor is just some silly test app. CodeCrusader \N MIT \N \N \N python \N \N \N \N 2023-05-18 11:29:07.176+00 2023-07-21 11:29:07.176+00 \N +80 Silly HackSimulator With HackSimulator, you can do interesting things with the sensors. CyberCipher \N MIT \N \N \N python \N \N \N \N 2023-01-31 11:29:07.177+00 2023-03-06 11:29:07.177+00 \N +81 Utility CodeCrunch With CodeCrunch, you can do interesting things with the sensors. TechTinker \N MIT \N \N \N python \N \N \N \N 2023-02-21 11:29:07.178+00 2023-05-27 11:29:07.178+00 \N +82 Uncategorised SecureCraft With SecureCraft, you can do interesting things with the sensors. ElectronExplorer \N MIT \N \N \N python \N \N \N \N 2024-01-29 11:29:07.179+00 2024-03-16 11:29:07.179+00 \N +83 SAO CryptoPulse With CryptoPulse, you can do interesting things with the sensors. PixelPulse \N MIT \N \N \N python \N \N \N \N 2023-01-18 11:29:07.181+00 2023-04-16 11:29:07.181+00 \N +84 Graphics DataForge With DataForge, you can do interesting things with the sensors. PixelPulse \N MIT \N \N \N python \N \N \N \N 2023-07-15 11:29:07.182+00 2023-07-24 11:29:07.182+00 \N +85 Utility CipherQuest With CipherQuest, you can do interesting things with the sensors. GameGladiator \N MIT \N \N \N python \N \N \N \N 2022-12-14 11:29:07.183+00 2022-12-24 11:29:07.183+00 \N +86 Hardware HackQuest With HackQuest, you can do interesting things with the sensors. CyberCipher \N MIT \N \N \N python \N \N \N \N 2023-08-16 11:29:07.185+00 2023-08-18 11:29:07.185+00 \N +87 Virus SecureSphere Use SecureSphere for some cool graphical effects. PixelPro \N MIT \N \N \N python \N \N \N \N 2023-12-26 11:29:07.186+00 2024-03-15 11:29:07.186+00 \N \. @@ -431,10 +442,10 @@ COPY badgehub.badge_project (id, badge_id, project_id, status, created_at, updat -- Data for Name: badges; Type: TABLE DATA; Schema: badgehub; Owner: badgehub -- -COPY badgehub.badges (id, name, slug, constraints, commands, deleted_at, created_at, updated_at) FROM stdin; -1 mch2022 mch2022 \N \N \N 2022-06-12 16:41:34+00 2022-06-12 16:41:48+00 -2 troopers23 troopers23 \N \N \N 2023-06-19 17:48:13+00 2023-06-19 17:48:13+00 -5 WHY2025 why2025 \N \N \N 2024-05-22 11:17:11.441719+00 2024-05-22 11:17:11.441719+00 +COPY badgehub.badges (slug, name, created_at, updated_at, deleted_at) FROM stdin; +mch2022 mch2022 2022-06-12 16:41:34+00 2022-06-12 16:41:48+00 \N +troopers23 troopers23 2023-06-19 17:48:13+00 2023-06-19 17:48:13+00 \N +why2025 WHY2025 2024-05-22 11:17:11.441719+00 2024-05-22 11:17:11.441719+00 \N \. @@ -442,30 +453,144 @@ COPY badgehub.badges (id, name, slug, constraints, commands, deleted_at, created -- Data for Name: categories; Type: TABLE DATA; Schema: badgehub; Owner: badgehub -- -COPY badgehub.categories (id, name, slug, deleted_at, created_at, updated_at, hidden) FROM stdin; -1 Uncategorised uncategorised \N 2022-06-27 17:06:25+00 2022-06-27 17:06:25+00 f -2 Event related event_related \N 2022-06-27 17:06:25+00 2022-06-27 17:06:25+00 f -3 Games games \N 2022-06-27 17:06:25+00 2022-06-27 17:06:25+00 f -4 Graphics graphics \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -5 Hardware hardware \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -6 Utility utility \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -7 Wearable wearable \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -8 Data data \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -9 Silly silly \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -10 Hacking hacking \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -11 Troll troll \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -12 Unusable unusable \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -13 Adult adult \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -14 Virus virus \N 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 f -15 SAO sao \N \N \N f +COPY badgehub.categories (slug, name, created_at, updated_at, deleted_at) FROM stdin; +uncategorised Uncategorised 2022-06-27 17:06:25+00 2022-06-27 17:06:25+00 \N +event_related Event related 2022-06-27 17:06:25+00 2022-06-27 17:06:25+00 \N +games Games 2022-06-27 17:06:25+00 2022-06-27 17:06:25+00 \N +graphics Graphics 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +hardware Hardware 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +utility Utility 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +wearable Wearable 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +data Data 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +silly Silly 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +hacking Hacking 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +troll Troll 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +unusable Unusable 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +adult Adult 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +virus Virus 2022-06-27 17:06:26+00 2022-06-27 17:06:26+00 \N +sao SAO \N \N \N \. -- --- Data for Name: dependencies; Type: TABLE DATA; Schema: badgehub; Owner: badgehub +-- Data for Name: migrations; Type: TABLE DATA; Schema: badgehub; Owner: badgehub -- -COPY badgehub.dependencies (project_id, depends_on_project_id, created_at, updated_at) FROM stdin; +COPY badgehub.migrations (id, name, run_on) FROM stdin; +1 /20241116085102-initialize 2024-11-27 23:56:35.535 +\. + + +-- +-- Data for Name: project_statuses_on_badges; Type: TABLE DATA; Schema: badgehub; Owner: badgehub +-- + +COPY badgehub.project_statuses_on_badges (id, project_slug, badge_slug, status, created_at, updated_at, deleted_at) FROM stdin; +1 codecraft why2025 unknown \N \N \N +2 codecraft troopers23 unknown \N \N \N +3 pixelpulse why2025 unknown \N \N \N +4 bitblast why2025 unknown \N \N \N +5 nanogames mch2022 unknown \N \N \N +6 nanogames troopers23 unknown \N \N \N +7 electraplay troopers23 unknown \N \N \N +8 circuitforge troopers23 unknown \N \N \N +9 bytebash mch2022 unknown \N \N \N +10 codecanvas troopers23 unknown \N \N \N +11 sparkscript why2025 unknown \N \N \N +12 logicland why2025 unknown \N \N \N +13 microarcade troopers23 unknown \N \N \N +14 codecraze troopers23 unknown \N \N \N +15 gamegenius mch2022 unknown \N \N \N +16 gamegenius troopers23 unknown \N \N \N +17 pixelpal troopers23 unknown \N \N \N +18 electronica troopers23 unknown \N \N \N +19 codequest why2025 unknown \N \N \N +20 circuitcraft mch2022 unknown \N \N \N +21 bytebeat why2025 unknown \N \N \N +22 bytebeat troopers23 unknown \N \N \N +23 nanonexus mch2022 unknown \N \N \N +24 nanonexus why2025 unknown \N \N \N +25 bitbox mch2022 unknown \N \N \N +26 circuitchaos troopers23 unknown \N \N \N +27 circuitchaos why2025 unknown \N \N \N +28 codecrafter mch2022 unknown \N \N \N +29 pixelpioneer troopers23 unknown \N \N \N +30 logiclab why2025 unknown \N \N \N +31 byteblitz troopers23 unknown \N \N \N +32 byteblitz why2025 unknown \N \N \N +33 codewave mch2022 unknown \N \N \N +34 codewave troopers23 unknown \N \N \N +35 nanonet why2025 unknown \N \N \N +36 electraforge why2025 unknown \N \N \N +37 electraforge mch2022 unknown \N \N \N +38 gamegrid troopers23 unknown \N \N \N +39 logicloom troopers23 unknown \N \N \N +40 pixelplaza troopers23 unknown \N \N \N +41 codecity why2025 unknown \N \N \N +42 nanoarcade mch2022 unknown \N \N \N +43 electronera why2025 unknown \N \N \N +44 bitbazaar why2025 unknown \N \N \N +45 logiclegends troopers23 unknown \N \N \N +46 codeclan why2025 unknown \N \N \N +47 codeclan mch2022 unknown \N \N \N +48 pixelportal troopers23 unknown \N \N \N +49 circuitcraze why2025 unknown \N \N \N +50 bytebuster mch2022 unknown \N \N \N +51 nanonovel mch2022 unknown \N \N \N +52 electraeden troopers23 unknown \N \N \N +53 electraeden mch2022 unknown \N \N \N +54 codecomet mch2022 unknown \N \N \N +55 pixelplayground why2025 unknown \N \N \N +56 logiclandia why2025 unknown \N \N \N +57 bytebounce mch2022 unknown \N \N \N +58 bytebounce why2025 unknown \N \N \N +59 circuitcarnival mch2022 unknown \N \N \N +60 codecove mch2022 unknown \N \N \N +61 codecove troopers23 unknown \N \N \N +62 nanonest why2025 unknown \N \N \N +63 electraentertain why2025 unknown \N \N \N +64 gamegalaxy mch2022 unknown \N \N \N +65 logiclabyrinth why2025 unknown \N \N \N +66 byteblaster mch2022 unknown \N \N \N +67 codecompass troopers23 unknown \N \N \N +68 nanonation mch2022 unknown \N \N \N +69 electraempire why2025 unknown \N \N \N +70 electraempire mch2022 unknown \N \N \N +71 gamegarden troopers23 unknown \N \N \N +72 pixelpeak troopers23 unknown \N \N \N +73 pixelpeak mch2022 unknown \N \N \N +74 circuitcelestial mch2022 unknown \N \N \N +75 codecrusade why2025 unknown \N \N \N +76 nanonebula why2025 unknown \N \N \N +77 electraenclave troopers23 unknown \N \N \N +78 gamegizmo why2025 unknown \N \N \N +79 pixelplanet mch2022 unknown \N \N \N +80 logiclounge troopers23 unknown \N \N \N +81 bytebeacon why2025 unknown \N \N \N +82 codecircus mch2022 unknown \N \N \N +83 nanonook why2025 unknown \N \N \N +84 nanonook mch2022 unknown \N \N \N +85 electraelysium troopers23 unknown \N \N \N +86 gameglimpse mch2022 unknown \N \N \N +87 pixelparadise mch2022 unknown \N \N \N +88 pixelparadise why2025 unknown \N \N \N +89 codecoast troopers23 unknown \N \N \N +90 nanonirvana why2025 unknown \N \N \N +91 electraedifice why2025 unknown \N \N \N +92 gamegen mch2022 unknown \N \N \N +93 gamegen why2025 unknown \N \N \N +94 pixelpandemonium troopers23 unknown \N \N \N +95 logiclagoon mch2022 unknown \N \N \N +96 byteblaze why2025 unknown \N \N \N +97 codecorridor mch2022 unknown \N \N \N +98 hacksimulator mch2022 unknown \N \N \N +99 codecrunch mch2022 unknown \N \N \N +100 securecraft troopers23 unknown \N \N \N +101 cryptopulse troopers23 unknown \N \N \N +102 dataforge mch2022 unknown \N \N \N +103 cipherquest why2025 unknown \N \N \N +104 hackquest why2025 unknown \N \N \N +105 securesphere why2025 unknown \N \N \N \. @@ -473,94 +598,94 @@ COPY badgehub.dependencies (project_id, depends_on_project_id, created_at, updat -- Data for Name: projects; Type: TABLE DATA; Schema: badgehub; Owner: badgehub -- -COPY badgehub.projects (id, category_id, user_id, name, slug, min_firmware, max_firmware, git, git_commit_id, published_at, deleted_at, created_at, updated_at, download_counter, allow_team_fixes, project_type, license, description) FROM stdin; -0 9 67 CodeCraft codecraft \N \N \N \N \N \N 2024-01-05 11:29:07.05+00 2024-03-20 11:29:07.05+00 0 t python MIT Make some magic happen with CodeCraft. -1 3 58 PixelPulse pixelpulse \N \N \N \N \N \N 2023-08-30 11:29:07.053+00 2023-10-29 11:29:07.053+00 0 t python MIT Make some magic happen with PixelPulse. -2 2 43 BitBlast bitblast \N \N \N \N \N \N 2023-09-22 11:29:07.055+00 2023-11-13 11:29:07.055+00 0 t python MIT Make some magic happen with BitBlast. -3 2 10 NanoGames nanogames \N \N \N \N \N \N 2022-10-24 11:29:07.056+00 2023-01-02 11:29:07.057+00 0 t python MIT Make some magic happen with NanoGames. -4 12 47 ElectraPlay electraplay \N \N \N \N \N \N 2023-09-04 11:29:07.058+00 2023-10-06 11:29:07.058+00 0 t python MIT With ElectraPlay, you can do interesting things with the sensors. -5 15 54 CircuitForge circuitforge \N \N \N \N \N \N 2023-01-28 11:29:07.06+00 2023-04-24 11:29:07.06+00 0 t python MIT With CircuitForge, you can do interesting things with the sensors. -6 2 38 ByteBash bytebash \N \N \N \N \N \N 2023-08-02 11:29:07.061+00 2023-10-27 11:29:07.061+00 0 t python MIT Use ByteBash for some cool graphical effects. -7 13 19 CodeCanvas codecanvas \N \N \N \N \N \N 2023-12-31 11:29:07.063+00 2024-02-09 11:29:07.063+00 0 t python MIT With CodeCanvas, you can do interesting things with the sensors. -8 12 23 SparkScript sparkscript \N \N \N \N \N \N 2022-12-13 11:29:07.064+00 2023-01-13 11:29:07.064+00 0 t python MIT Make some magic happen with SparkScript. -9 8 38 LogicLand logicland \N \N \N \N \N \N 2023-02-05 11:29:07.066+00 2023-05-13 11:29:07.066+00 0 t python MIT Use LogicLand for some cool graphical effects. -10 2 13 MicroArcade microarcade \N \N \N \N \N \N 2024-01-05 11:29:07.069+00 2024-03-01 11:29:07.069+00 0 t python MIT MicroArcade is just some silly test app. -11 15 11 CodeCraze codecraze \N \N \N \N \N \N 2024-05-07 11:29:07.071+00 2024-07-22 11:29:07.071+00 0 t python MIT Use CodeCraze for some cool graphical effects. -12 15 23 GameGenius gamegenius \N \N \N \N \N \N 2023-12-14 11:29:07.073+00 2024-01-22 11:29:07.073+00 0 t python MIT GameGenius is just some silly test app. -13 7 54 PixelPal pixelpal \N \N \N \N \N \N 2023-08-10 11:29:07.075+00 2023-10-23 11:29:07.075+00 0 t python MIT Make some magic happen with PixelPal. -14 4 15 Electronica electronica \N \N \N \N \N \N 2024-02-26 11:29:07.078+00 2024-04-29 11:29:07.078+00 0 t python MIT Use Electronica for some cool graphical effects. -15 2 22 CodeQuest codequest \N \N \N \N \N \N 2023-05-15 11:29:07.08+00 2023-07-04 11:29:07.08+00 0 t python MIT Use CodeQuest for some cool graphical effects. -16 11 7 CircuitCraft circuitcraft \N \N \N \N \N \N 2023-06-19 11:29:07.082+00 2023-07-20 11:29:07.083+00 0 t python MIT Make some magic happen with CircuitCraft. -17 11 56 ByteBeat bytebeat \N \N \N \N \N \N 2023-06-07 11:29:07.084+00 2023-07-30 11:29:07.084+00 0 t python MIT ByteBeat is just some silly test app. -18 8 59 NanoNexus nanonexus \N \N \N \N \N \N 2023-06-28 11:29:07.087+00 2023-07-16 11:29:07.087+00 0 t python MIT With NanoNexus, you can do interesting things with the sensors. -19 2 16 BitBox bitbox \N \N \N \N \N \N 2023-10-15 11:29:07.089+00 2023-10-21 11:29:07.089+00 0 t python MIT With BitBox, you can do interesting things with the sensors. -20 12 38 CircuitChaos circuitchaos \N \N \N \N \N \N 2023-02-08 11:29:07.09+00 2023-03-11 11:29:07.09+00 0 t python MIT Use CircuitChaos for some cool graphical effects. -21 8 34 CodeCrafter codecrafter \N \N \N \N \N \N 2024-04-16 11:29:07.092+00 2024-04-28 11:29:07.092+00 0 t python MIT Use CodeCrafter for some cool graphical effects. -22 14 65 PixelPioneer pixelpioneer \N \N \N \N \N \N 2022-12-07 11:29:07.094+00 2023-02-18 11:29:07.094+00 0 t python MIT PixelPioneer is just some silly test app. -23 12 17 LogicLab logiclab \N \N \N \N \N \N 2023-05-14 11:29:07.096+00 2023-08-16 11:29:07.096+00 0 t python MIT Use LogicLab for some cool graphical effects. -24 8 31 ByteBlitz byteblitz \N \N \N \N \N \N 2024-03-29 11:29:07.099+00 2024-05-23 11:29:07.099+00 0 t python MIT Make some magic happen with ByteBlitz. -25 14 17 CodeWave codewave \N \N \N \N \N \N 2024-03-09 11:29:07.1+00 2024-06-07 11:29:07.1+00 0 t python MIT Use CodeWave for some cool graphical effects. -26 2 64 NanoNet nanonet \N \N \N \N \N \N 2023-11-16 11:29:07.102+00 2024-01-16 11:29:07.102+00 0 t python MIT NanoNet is just some silly test app. -27 7 64 ElectraForge electraforge \N \N \N \N \N \N 2023-04-21 11:29:07.103+00 2023-06-29 11:29:07.104+00 0 t python MIT Make some magic happen with ElectraForge. -28 7 4 GameGrid gamegrid \N \N \N \N \N \N 2022-10-22 11:29:07.105+00 2022-12-09 11:29:07.105+00 0 t python MIT With GameGrid, you can do interesting things with the sensors. -29 9 15 LogicLoom logicloom \N \N \N \N \N \N 2023-08-28 11:29:07.106+00 2023-10-03 11:29:07.106+00 0 t python MIT With LogicLoom, you can do interesting things with the sensors. -30 9 2 PixelPlaza pixelplaza \N \N \N \N \N \N 2023-06-27 11:29:07.108+00 2023-06-28 11:29:07.108+00 0 t python MIT Use PixelPlaza for some cool graphical effects. -31 2 58 CodeCity codecity \N \N \N \N \N \N 2022-10-01 11:29:07.11+00 2022-10-27 11:29:07.11+00 0 t python MIT CodeCity is just some silly test app. -32 11 12 NanoArcade nanoarcade \N \N \N \N \N \N 2023-07-24 11:29:07.112+00 2023-08-11 11:29:07.112+00 0 t python MIT Use NanoArcade for some cool graphical effects. -33 13 14 ElectronEra electronera \N \N \N \N \N \N 2023-11-12 11:29:07.114+00 2024-01-17 11:29:07.114+00 0 t python MIT With ElectronEra, you can do interesting things with the sensors. -34 11 7 BitBazaar bitbazaar \N \N \N \N \N \N 2022-12-12 11:29:07.115+00 2023-02-13 11:29:07.115+00 0 t python MIT Make some magic happen with BitBazaar. -35 15 44 LogicLegends logiclegends \N \N \N \N \N \N 2023-03-16 11:29:07.116+00 2023-05-22 11:29:07.116+00 0 t python MIT LogicLegends is just some silly test app. -36 8 33 CodeClan codeclan \N \N \N \N \N \N 2023-11-28 11:29:07.118+00 2024-01-06 11:29:07.118+00 0 t python MIT Use CodeClan for some cool graphical effects. -37 13 2 PixelPortal pixelportal \N \N \N \N \N \N 2024-02-12 11:29:07.119+00 2024-04-25 11:29:07.119+00 0 t python MIT PixelPortal is just some silly test app. -38 13 7 CircuitCraze circuitcraze \N \N \N \N \N \N 2022-11-04 11:29:07.12+00 2023-01-22 11:29:07.12+00 0 t python MIT With CircuitCraze, you can do interesting things with the sensors. -39 6 69 ByteBuster bytebuster \N \N \N \N \N \N 2022-12-09 11:29:07.122+00 2022-12-24 11:29:07.122+00 0 t python MIT With ByteBuster, you can do interesting things with the sensors. -40 9 12 NanoNovel nanonovel \N \N \N \N \N \N 2024-03-17 11:29:07.123+00 2024-05-14 11:29:07.123+00 0 t python MIT Make some magic happen with NanoNovel. -41 3 46 ElectraEden electraeden \N \N \N \N \N \N 2023-05-19 11:29:07.125+00 2023-06-03 11:29:07.125+00 0 t python MIT Use ElectraEden for some cool graphical effects. -42 13 34 CodeComet codecomet \N \N \N \N \N \N 2024-02-25 11:29:07.126+00 2024-03-05 11:29:07.126+00 0 t python MIT With CodeComet, you can do interesting things with the sensors. -43 15 28 PixelPlayground pixelplayground \N \N \N \N \N \N 2023-02-15 11:29:07.128+00 2023-05-12 11:29:07.128+00 0 t python MIT Make some magic happen with PixelPlayground. -44 1 70 LogicLandia logiclandia \N \N \N \N \N \N 2023-08-10 11:29:07.129+00 2023-09-23 11:29:07.129+00 0 t python MIT Use LogicLandia for some cool graphical effects. -45 9 7 ByteBounce bytebounce \N \N \N \N \N \N 2024-03-02 11:29:07.13+00 2024-05-15 11:29:07.13+00 0 t python MIT With ByteBounce, you can do interesting things with the sensors. -46 3 40 CircuitCarnival circuitcarnival \N \N \N \N \N \N 2023-02-03 11:29:07.131+00 2023-03-10 11:29:07.131+00 0 t python MIT Make some magic happen with CircuitCarnival. -47 15 48 CodeCove codecove \N \N \N \N \N \N 2022-10-15 11:29:07.132+00 2022-12-05 11:29:07.132+00 0 t python MIT Use CodeCove for some cool graphical effects. -48 3 16 NanoNest nanonest \N \N \N \N \N \N 2023-10-05 11:29:07.133+00 2023-11-12 11:29:07.134+00 0 t python MIT With NanoNest, you can do interesting things with the sensors. -49 1 48 ElectraEntertain electraentertain \N \N \N \N \N \N 2023-07-11 11:29:07.135+00 2023-09-21 11:29:07.135+00 0 t python MIT ElectraEntertain is just some silly test app. -50 13 50 GameGalaxy gamegalaxy \N \N \N \N \N \N 2024-02-02 11:29:07.136+00 2024-05-05 11:29:07.136+00 0 t python MIT Use GameGalaxy for some cool graphical effects. -51 12 3 LogicLabyrinth logiclabyrinth \N \N \N \N \N \N 2024-01-04 11:29:07.138+00 2024-01-04 11:29:07.138+00 0 t python MIT Make some magic happen with LogicLabyrinth. -52 12 28 ByteBlaster byteblaster \N \N \N \N \N \N 2022-10-12 11:29:07.14+00 2022-11-07 11:29:07.14+00 0 t python MIT ByteBlaster is just some silly test app. -53 8 6 CodeCompass codecompass \N \N \N \N \N \N 2023-02-18 11:29:07.142+00 2023-03-20 11:29:07.142+00 0 t python MIT CodeCompass is just some silly test app. -54 9 26 NanoNation nanonation \N \N \N \N \N \N 2024-03-06 11:29:07.143+00 2024-06-11 11:29:07.143+00 0 t python MIT Use NanoNation for some cool graphical effects. -55 9 37 ElectraEmpire electraempire \N \N \N \N \N \N 2023-04-17 11:29:07.144+00 2023-06-14 11:29:07.145+00 0 t python MIT ElectraEmpire is just some silly test app. -56 13 3 GameGarden gamegarden \N \N \N \N \N \N 2024-04-04 11:29:07.146+00 2024-04-29 11:29:07.146+00 0 t python MIT Make some magic happen with GameGarden. -57 4 34 PixelPeak pixelpeak \N \N \N \N \N \N 2023-06-13 11:29:07.147+00 2023-07-13 11:29:07.147+00 0 t python MIT With PixelPeak, you can do interesting things with the sensors. -58 3 55 CircuitCelestial circuitcelestial \N \N \N \N \N \N 2023-07-14 11:29:07.149+00 2023-07-19 11:29:07.149+00 0 t python MIT CircuitCelestial is just some silly test app. -59 5 30 CodeCrusade codecrusade \N \N \N \N \N \N 2023-10-09 11:29:07.15+00 2023-12-23 11:29:07.15+00 0 t python MIT CodeCrusade is just some silly test app. -60 12 16 NanoNebula nanonebula \N \N \N \N \N \N 2023-03-03 11:29:07.151+00 2023-05-22 11:29:07.151+00 0 t python MIT NanoNebula is just some silly test app. -61 6 3 ElectraEnclave electraenclave \N \N \N \N \N \N 2023-09-18 11:29:07.153+00 2023-10-07 11:29:07.153+00 0 t python MIT Use ElectraEnclave for some cool graphical effects. -62 4 7 GameGizmo gamegizmo \N \N \N \N \N \N 2023-12-04 11:29:07.155+00 2024-01-17 11:29:07.155+00 0 t python MIT Use GameGizmo for some cool graphical effects. -63 15 57 PixelPlanet pixelplanet \N \N \N \N \N \N 2023-02-04 11:29:07.156+00 2023-04-06 11:29:07.156+00 0 t python MIT With PixelPlanet, you can do interesting things with the sensors. -64 4 55 LogicLounge logiclounge \N \N \N \N \N \N 2022-12-24 11:29:07.157+00 2023-03-02 11:29:07.157+00 0 t python MIT Make some magic happen with LogicLounge. -65 9 9 ByteBeacon bytebeacon \N \N \N \N \N \N 2024-01-25 11:29:07.158+00 2024-02-04 11:29:07.158+00 0 t python MIT Use ByteBeacon for some cool graphical effects. -66 5 52 CodeCircus codecircus \N \N \N \N \N \N 2023-07-04 11:29:07.16+00 2023-08-14 11:29:07.16+00 0 t python MIT CodeCircus is just some silly test app. -67 13 41 NanoNook nanonook \N \N \N \N \N \N 2022-12-14 11:29:07.161+00 2023-03-12 11:29:07.161+00 0 t python MIT Make some magic happen with NanoNook. -68 1 55 ElectraElysium electraelysium \N \N \N \N \N \N 2023-06-25 11:29:07.163+00 2023-07-28 11:29:07.163+00 0 t python MIT Make some magic happen with ElectraElysium. -69 4 51 GameGlimpse gameglimpse \N \N \N \N \N \N 2023-01-24 11:29:07.164+00 2023-03-12 11:29:07.164+00 0 t python MIT GameGlimpse is just some silly test app. -70 14 38 PixelParadise pixelparadise \N \N \N \N \N \N 2023-03-02 11:29:07.165+00 2023-03-22 11:29:07.165+00 0 t python MIT PixelParadise is just some silly test app. -71 15 59 CodeCoast codecoast \N \N \N \N \N \N 2024-01-18 11:29:07.167+00 2024-02-04 11:29:07.167+00 0 t python MIT With CodeCoast, you can do interesting things with the sensors. -72 6 59 NanoNirvana nanonirvana \N \N \N \N \N \N 2023-06-18 11:29:07.168+00 2023-07-18 11:29:07.168+00 0 t python MIT NanoNirvana is just some silly test app. -73 6 55 ElectraEdifice electraedifice \N \N \N \N \N \N 2022-11-15 11:29:07.17+00 2022-11-25 11:29:07.17+00 0 t python MIT Make some magic happen with ElectraEdifice. -74 10 55 GameGen gamegen \N \N \N \N \N \N 2023-01-16 11:29:07.171+00 2023-03-02 11:29:07.171+00 0 t python MIT GameGen is just some silly test app. -75 7 44 PixelPandemonium pixelpandemonium \N \N \N \N \N \N 2023-12-21 11:29:07.172+00 2024-03-23 11:29:07.172+00 0 t python MIT Make some magic happen with PixelPandemonium. -76 9 46 LogicLagoon logiclagoon \N \N \N \N \N \N 2023-11-25 11:29:07.174+00 2023-12-13 11:29:07.174+00 0 t python MIT Use LogicLagoon for some cool graphical effects. -77 3 3 ByteBlaze byteblaze \N \N \N \N \N \N 2023-03-20 11:29:07.175+00 2023-04-05 11:29:07.175+00 0 t python MIT With ByteBlaze, you can do interesting things with the sensors. -78 9 46 CodeCorridor codecorridor \N \N \N \N \N \N 2023-05-18 11:29:07.176+00 2023-07-21 11:29:07.176+00 0 t python MIT CodeCorridor is just some silly test app. -79 9 37 HackSimulator hacksimulator \N \N \N \N \N \N 2023-01-31 11:29:07.177+00 2023-03-06 11:29:07.177+00 0 t python MIT With HackSimulator, you can do interesting things with the sensors. -80 6 45 CodeCrunch codecrunch \N \N \N \N \N \N 2023-02-21 11:29:07.178+00 2023-05-27 11:29:07.178+00 0 t python MIT With CodeCrunch, you can do interesting things with the sensors. -81 1 23 SecureCraft securecraft \N \N \N \N \N \N 2024-01-29 11:29:07.179+00 2024-03-16 11:29:07.179+00 0 t python MIT With SecureCraft, you can do interesting things with the sensors. -82 15 12 CryptoPulse cryptopulse \N \N \N \N \N \N 2023-01-18 11:29:07.181+00 2023-04-16 11:29:07.181+00 0 t python MIT With CryptoPulse, you can do interesting things with the sensors. -83 4 12 DataForge dataforge \N \N \N \N \N \N 2023-07-15 11:29:07.182+00 2023-07-24 11:29:07.182+00 0 t python MIT With DataForge, you can do interesting things with the sensors. -84 6 50 CipherQuest cipherquest \N \N \N \N \N \N 2022-12-14 11:29:07.183+00 2022-12-24 11:29:07.183+00 0 t python MIT With CipherQuest, you can do interesting things with the sensors. -85 5 37 HackQuest hackquest \N \N \N \N \N \N 2023-08-16 11:29:07.185+00 2023-08-18 11:29:07.185+00 0 t python MIT With HackQuest, you can do interesting things with the sensors. -86 14 54 SecureSphere securesphere \N \N \N \N \N \N 2023-12-26 11:29:07.186+00 2024-03-15 11:29:07.186+00 0 t python MIT Use SecureSphere for some cool graphical effects. +COPY badgehub.projects (created_at, updated_at, deleted_at, version_id, user_id, slug, git, allow_team_fixes) FROM stdin; +2024-01-05 11:29:07.05+00 2024-03-20 11:29:07.05+00 \N 1 67 codecraft \N \N +2023-08-30 11:29:07.053+00 2023-10-29 11:29:07.053+00 \N 2 58 pixelpulse \N \N +2023-09-22 11:29:07.055+00 2023-11-13 11:29:07.055+00 \N 3 43 bitblast \N \N +2022-10-24 11:29:07.056+00 2023-01-02 11:29:07.057+00 \N 4 10 nanogames \N \N +2023-09-04 11:29:07.058+00 2023-10-06 11:29:07.058+00 \N 5 47 electraplay \N \N +2023-01-28 11:29:07.06+00 2023-04-24 11:29:07.06+00 \N 6 54 circuitforge \N \N +2023-08-02 11:29:07.061+00 2023-10-27 11:29:07.061+00 \N 7 38 bytebash \N \N +2023-12-31 11:29:07.063+00 2024-02-09 11:29:07.063+00 \N 8 19 codecanvas \N \N +2022-12-13 11:29:07.064+00 2023-01-13 11:29:07.064+00 \N 9 23 sparkscript \N \N +2023-02-05 11:29:07.066+00 2023-05-13 11:29:07.066+00 \N 10 38 logicland \N \N +2024-01-05 11:29:07.069+00 2024-03-01 11:29:07.069+00 \N 11 13 microarcade \N \N +2024-05-07 11:29:07.071+00 2024-07-22 11:29:07.071+00 \N 12 11 codecraze \N \N +2023-12-14 11:29:07.073+00 2024-01-22 11:29:07.073+00 \N 13 23 gamegenius \N \N +2023-08-10 11:29:07.075+00 2023-10-23 11:29:07.075+00 \N 14 54 pixelpal \N \N +2024-02-26 11:29:07.078+00 2024-04-29 11:29:07.078+00 \N 15 15 electronica \N \N +2023-05-15 11:29:07.08+00 2023-07-04 11:29:07.08+00 \N 16 22 codequest \N \N +2023-06-19 11:29:07.082+00 2023-07-20 11:29:07.083+00 \N 17 7 circuitcraft \N \N +2023-06-07 11:29:07.084+00 2023-07-30 11:29:07.084+00 \N 18 56 bytebeat \N \N +2023-06-28 11:29:07.087+00 2023-07-16 11:29:07.087+00 \N 19 59 nanonexus \N \N +2023-10-15 11:29:07.089+00 2023-10-21 11:29:07.089+00 \N 20 16 bitbox \N \N +2023-02-08 11:29:07.09+00 2023-03-11 11:29:07.09+00 \N 21 38 circuitchaos \N \N +2024-04-16 11:29:07.092+00 2024-04-28 11:29:07.092+00 \N 22 34 codecrafter \N \N +2022-12-07 11:29:07.094+00 2023-02-18 11:29:07.094+00 \N 23 65 pixelpioneer \N \N +2023-05-14 11:29:07.096+00 2023-08-16 11:29:07.096+00 \N 24 17 logiclab \N \N +2024-03-29 11:29:07.099+00 2024-05-23 11:29:07.099+00 \N 25 31 byteblitz \N \N +2024-03-09 11:29:07.1+00 2024-06-07 11:29:07.1+00 \N 26 17 codewave \N \N +2023-11-16 11:29:07.102+00 2024-01-16 11:29:07.102+00 \N 27 64 nanonet \N \N +2023-04-21 11:29:07.103+00 2023-06-29 11:29:07.104+00 \N 28 64 electraforge \N \N +2022-10-22 11:29:07.105+00 2022-12-09 11:29:07.105+00 \N 29 4 gamegrid \N \N +2023-08-28 11:29:07.106+00 2023-10-03 11:29:07.106+00 \N 30 15 logicloom \N \N +2023-06-27 11:29:07.108+00 2023-06-28 11:29:07.108+00 \N 31 2 pixelplaza \N \N +2022-10-01 11:29:07.11+00 2022-10-27 11:29:07.11+00 \N 32 58 codecity \N \N +2023-07-24 11:29:07.112+00 2023-08-11 11:29:07.112+00 \N 33 12 nanoarcade \N \N +2023-11-12 11:29:07.114+00 2024-01-17 11:29:07.114+00 \N 34 14 electronera \N \N +2022-12-12 11:29:07.115+00 2023-02-13 11:29:07.115+00 \N 35 7 bitbazaar \N \N +2023-03-16 11:29:07.116+00 2023-05-22 11:29:07.116+00 \N 36 44 logiclegends \N \N +2023-11-28 11:29:07.118+00 2024-01-06 11:29:07.118+00 \N 37 33 codeclan \N \N +2024-02-12 11:29:07.119+00 2024-04-25 11:29:07.119+00 \N 38 2 pixelportal \N \N +2022-11-04 11:29:07.12+00 2023-01-22 11:29:07.12+00 \N 39 7 circuitcraze \N \N +2022-12-09 11:29:07.122+00 2022-12-24 11:29:07.122+00 \N 40 69 bytebuster \N \N +2024-03-17 11:29:07.123+00 2024-05-14 11:29:07.123+00 \N 41 12 nanonovel \N \N +2023-05-19 11:29:07.125+00 2023-06-03 11:29:07.125+00 \N 42 46 electraeden \N \N +2024-02-25 11:29:07.126+00 2024-03-05 11:29:07.126+00 \N 43 34 codecomet \N \N +2023-02-15 11:29:07.128+00 2023-05-12 11:29:07.128+00 \N 44 28 pixelplayground \N \N +2023-08-10 11:29:07.129+00 2023-09-23 11:29:07.129+00 \N 45 70 logiclandia \N \N +2024-03-02 11:29:07.13+00 2024-05-15 11:29:07.13+00 \N 46 7 bytebounce \N \N +2023-02-03 11:29:07.131+00 2023-03-10 11:29:07.131+00 \N 47 40 circuitcarnival \N \N +2022-10-15 11:29:07.132+00 2022-12-05 11:29:07.132+00 \N 48 48 codecove \N \N +2023-10-05 11:29:07.133+00 2023-11-12 11:29:07.134+00 \N 49 16 nanonest \N \N +2023-07-11 11:29:07.135+00 2023-09-21 11:29:07.135+00 \N 50 48 electraentertain \N \N +2024-02-02 11:29:07.136+00 2024-05-05 11:29:07.136+00 \N 51 50 gamegalaxy \N \N +2024-01-04 11:29:07.138+00 2024-01-04 11:29:07.138+00 \N 52 3 logiclabyrinth \N \N +2022-10-12 11:29:07.14+00 2022-11-07 11:29:07.14+00 \N 53 28 byteblaster \N \N +2023-02-18 11:29:07.142+00 2023-03-20 11:29:07.142+00 \N 54 6 codecompass \N \N +2024-03-06 11:29:07.143+00 2024-06-11 11:29:07.143+00 \N 55 26 nanonation \N \N +2023-04-17 11:29:07.144+00 2023-06-14 11:29:07.145+00 \N 56 37 electraempire \N \N +2024-04-04 11:29:07.146+00 2024-04-29 11:29:07.146+00 \N 57 3 gamegarden \N \N +2023-06-13 11:29:07.147+00 2023-07-13 11:29:07.147+00 \N 58 34 pixelpeak \N \N +2023-07-14 11:29:07.149+00 2023-07-19 11:29:07.149+00 \N 59 55 circuitcelestial \N \N +2023-10-09 11:29:07.15+00 2023-12-23 11:29:07.15+00 \N 60 30 codecrusade \N \N +2023-03-03 11:29:07.151+00 2023-05-22 11:29:07.151+00 \N 61 16 nanonebula \N \N +2023-09-18 11:29:07.153+00 2023-10-07 11:29:07.153+00 \N 62 3 electraenclave \N \N +2023-12-04 11:29:07.155+00 2024-01-17 11:29:07.155+00 \N 63 7 gamegizmo \N \N +2023-02-04 11:29:07.156+00 2023-04-06 11:29:07.156+00 \N 64 57 pixelplanet \N \N +2022-12-24 11:29:07.157+00 2023-03-02 11:29:07.157+00 \N 65 55 logiclounge \N \N +2024-01-25 11:29:07.158+00 2024-02-04 11:29:07.158+00 \N 66 9 bytebeacon \N \N +2023-07-04 11:29:07.16+00 2023-08-14 11:29:07.16+00 \N 67 52 codecircus \N \N +2022-12-14 11:29:07.161+00 2023-03-12 11:29:07.161+00 \N 68 41 nanonook \N \N +2023-06-25 11:29:07.163+00 2023-07-28 11:29:07.163+00 \N 69 55 electraelysium \N \N +2023-01-24 11:29:07.164+00 2023-03-12 11:29:07.164+00 \N 70 51 gameglimpse \N \N +2023-03-02 11:29:07.165+00 2023-03-22 11:29:07.165+00 \N 71 38 pixelparadise \N \N +2024-01-18 11:29:07.167+00 2024-02-04 11:29:07.167+00 \N 72 59 codecoast \N \N +2023-06-18 11:29:07.168+00 2023-07-18 11:29:07.168+00 \N 73 59 nanonirvana \N \N +2022-11-15 11:29:07.17+00 2022-11-25 11:29:07.17+00 \N 74 55 electraedifice \N \N +2023-01-16 11:29:07.171+00 2023-03-02 11:29:07.171+00 \N 75 55 gamegen \N \N +2023-12-21 11:29:07.172+00 2024-03-23 11:29:07.172+00 \N 76 44 pixelpandemonium \N \N +2023-11-25 11:29:07.174+00 2023-12-13 11:29:07.174+00 \N 77 46 logiclagoon \N \N +2023-03-20 11:29:07.175+00 2023-04-05 11:29:07.175+00 \N 78 3 byteblaze \N \N +2023-05-18 11:29:07.176+00 2023-07-21 11:29:07.176+00 \N 79 46 codecorridor \N \N +2023-01-31 11:29:07.177+00 2023-03-06 11:29:07.177+00 \N 80 37 hacksimulator \N \N +2023-02-21 11:29:07.178+00 2023-05-27 11:29:07.178+00 \N 81 45 codecrunch \N \N +2024-01-29 11:29:07.179+00 2024-03-16 11:29:07.179+00 \N 82 23 securecraft \N \N +2023-01-18 11:29:07.181+00 2023-04-16 11:29:07.181+00 \N 83 12 cryptopulse \N \N +2023-07-15 11:29:07.182+00 2023-07-24 11:29:07.182+00 \N 84 12 dataforge \N \N +2022-12-14 11:29:07.183+00 2022-12-24 11:29:07.183+00 \N 85 50 cipherquest \N \N +2023-08-16 11:29:07.185+00 2023-08-18 11:29:07.185+00 \N 86 37 hackquest \N \N +2023-12-26 11:29:07.186+00 2024-03-15 11:29:07.186+00 \N 87 54 securesphere \N \N \. @@ -568,256 +693,417 @@ COPY badgehub.projects (id, category_id, user_id, name, slug, min_firmware, max_ -- Data for Name: users; Type: TABLE DATA; Schema: badgehub; Owner: badgehub -- -COPY badgehub.users (id, admin, name, email, email_verified_at, password, remember_token, editor, public, show_projects, google2fa_enabled, google2fa_secret, deleted_at, created_at, updated_at) FROM stdin; -0 f TechTinkerer techtinkerer@hotmail.com \N **** \N default t t f \N \N 2023-06-01 11:29:06.891+00 2023-08-19 11:29:06.9+00 -1 f CodeCrafter codecrafter@hotmail.com \N **** \N default t t f \N \N 2023-12-19 11:29:06.912+00 2024-01-07 11:29:06.913+00 -2 f PixelPilot pixelpilot@gmail.com \N **** \N default t t f \N \N 2022-11-29 11:29:06.917+00 2023-01-17 11:29:06.918+00 -3 f LogicLion logiclion@techinc.nl \N **** \N default t t f \N \N 2024-02-14 11:29:06.92+00 2024-03-20 11:29:06.921+00 -4 f ElectronEager electroneager@hackalot.nl \N **** \N default t t f \N \N 2024-02-08 11:29:06.923+00 2024-02-26 11:29:06.924+00 -5 f NanoNomad nanonomad@hack42.nl \N **** \N default f t f \N \N 2024-05-13 11:29:06.927+00 2024-06-13 11:29:06.927+00 -6 t CircuitCraze circuitcraze@hotmail.com \N **** \N default t f f \N \N 2024-01-27 11:29:06.931+00 2024-03-14 11:29:06.932+00 -7 f GameGlider gameglider@techinc.nl \N **** \N default t t f \N \N 2023-09-14 11:29:06.934+00 2023-11-27 11:29:06.934+00 -8 f ByteBlast byteblast@hotmail.com \N **** \N default t f f \N \N 2022-11-20 11:29:06.937+00 2022-12-05 11:29:06.937+00 -9 f CyberCraft cybercraft@hotmail.com \N **** \N default t t f \N \N 2023-03-11 11:29:06.94+00 2023-05-14 11:29:06.941+00 -10 f DigitalDynamo digitaldynamo@hackalot.nl \N **** \N default t t f \N \N 2024-04-15 11:29:06.944+00 2024-04-22 11:29:06.944+00 -11 f CodeCreator codecreator@hotmail.com \N **** \N default t f f \N \N 2023-10-12 11:29:06.946+00 2023-12-11 11:29:06.946+00 -12 f PixelPulse pixelpulse@gmail.com \N **** \N default f f f \N \N 2024-03-27 11:29:06.948+00 2024-05-08 11:29:06.948+00 -13 t LogicLuminary logicluminary@hack42.nl \N **** \N default t t f \N \N 2022-12-18 11:29:06.949+00 2023-02-16 11:29:06.949+00 -14 f ElectronEcho electronecho@gmail.com \N **** \N default t t f \N \N 2024-03-08 11:29:06.951+00 2024-06-04 11:29:06.951+00 -15 f NanoNinja nanoninja@bitlair.nl \N **** \N default f t f \N \N 2023-03-29 11:29:06.953+00 2023-06-26 11:29:06.953+00 -16 f CircuitChampion circuitchampion@gmail.com \N **** \N default t t f \N \N 2024-01-05 11:29:06.955+00 2024-01-19 11:29:06.955+00 -17 t GameGazer gamegazer@hackalot.nl \N **** \N default t t f \N \N 2022-11-23 11:29:06.956+00 2023-02-08 11:29:06.956+00 -18 f ByteBuddy bytebuddy@hack42.nl \N **** \N default t t f \N \N 2022-12-08 11:29:06.957+00 2023-01-28 11:29:06.957+00 -19 f TechTornado techtornado@techinc.nl \N **** \N default t t f \N \N 2023-08-20 11:29:06.959+00 2023-11-27 11:29:06.959+00 -20 f CodeChampion codechampion@hackalot.nl \N **** \N default t t f \N \N 2022-11-10 11:29:06.96+00 2022-11-20 11:29:06.961+00 -21 t PixelProdigy pixelprodigy@techinc.nl \N **** \N default t t f \N \N 2023-11-01 11:29:06.962+00 2023-12-06 11:29:06.962+00 -22 f LogicLabyrinth logiclabyrinth@techinc.nl \N **** \N default t t f \N \N 2024-05-21 11:29:06.963+00 2024-06-04 11:29:06.964+00 -23 f ElectronExplorer electronexplorer@bitlair.nl \N **** \N default t t f \N \N 2023-04-01 11:29:06.965+00 2023-05-25 11:29:06.965+00 -24 f NanoNavigator nanonavigator@gmail.com \N **** \N default t t f \N \N 2022-10-02 11:29:06.967+00 2022-10-04 11:29:06.967+00 -25 f CircuitCatalyst circuitcatalyst@bitlair.nl \N **** \N default f t f \N \N 2023-05-16 11:29:06.968+00 2023-08-13 11:29:06.968+00 -26 f GameGuru gameguru@hackalot.nl \N **** \N default t t f \N \N 2024-05-18 11:29:06.97+00 2024-07-15 11:29:06.97+00 -27 f ByteBlaze byteblaze@gmail.com \N **** \N default t t f \N \N 2023-12-29 11:29:06.971+00 2024-01-22 11:29:06.971+00 -28 f DigitalDreamer digitaldreamer@bitlair.nl \N **** \N default t t f \N \N 2024-03-24 11:29:06.973+00 2024-04-08 11:29:06.973+00 -29 f CodeCommander codecommander@hotmail.com \N **** \N default t t f \N \N 2022-11-16 11:29:06.974+00 2023-01-05 11:29:06.974+00 -30 f PixelPioneer pixelpioneer@bitlair.nl \N **** \N default f t f \N \N 2023-08-09 11:29:06.976+00 2023-11-06 11:29:06.976+00 -31 f LogicLegend logiclegend@hackalot.nl \N **** \N default t t f \N \N 2024-01-07 11:29:06.977+00 2024-03-14 11:29:06.977+00 -32 f ElectronElite electronelite@hotmail.com \N **** \N default t t f \N \N 2023-12-30 11:29:06.978+00 2024-01-07 11:29:06.978+00 -33 f NanoNerd nanonerd@gmail.com \N **** \N default t t f \N \N 2023-11-04 11:29:06.98+00 2023-11-04 11:29:06.98+00 -34 f CircuitCaptain circuitcaptain@hack42.nl \N **** \N default t t f \N \N 2024-01-19 11:29:06.981+00 2024-04-12 11:29:06.981+00 -35 f GameGenius gamegenius@gmail.com \N **** \N default t f f \N \N 2023-06-11 11:29:06.983+00 2023-09-14 11:29:06.983+00 -36 f ByteBolt bytebolt@techinc.nl \N **** \N default t t f \N \N 2023-12-11 11:29:06.984+00 2024-01-14 11:29:06.984+00 -37 f CyberCipher cybercipher@hackalot.nl \N **** \N default t t f \N \N 2022-10-01 11:29:06.986+00 2022-10-05 11:29:06.986+00 -38 f CodeConqueror codeconqueror@hack42.nl \N **** \N default f t f \N \N 2023-09-26 11:29:06.988+00 2023-10-18 11:29:06.988+00 -39 f PixelPaladin pixelpaladin@hack42.nl \N **** \N default t t f \N \N 2022-11-02 11:29:06.989+00 2022-12-24 11:29:06.99+00 -40 f LogicLore logiclore@techinc.nl \N **** \N default t t f \N \N 2024-04-15 11:29:06.992+00 2024-07-01 11:29:06.992+00 -41 f ElectronEnigma electronenigma@gmail.com \N **** \N default t t f \N \N 2022-10-01 11:29:06.993+00 2022-10-24 11:29:06.993+00 -42 f CircuitConnoisseur circuitconnoisseur@gmail.com \N **** \N default f t f \N \N 2022-10-26 11:29:06.996+00 2022-12-16 11:29:06.996+00 -43 f GameGuardian gameguardian@techinc.nl \N **** \N default t t f \N \N 2023-07-16 11:29:06.998+00 2023-10-11 11:29:06.998+00 -44 f ByteBandit bytebandit@gmail.com \N **** \N default t t f \N \N 2023-01-08 11:29:07+00 2023-01-15 11:29:07+00 -45 t TechTinker techtinker@techinc.nl \N **** \N default t t f \N \N 2023-10-05 11:29:07.002+00 2023-11-17 11:29:07.002+00 -46 f CodeCrusader codecrusader@hotmail.com \N **** \N default t t f \N \N 2024-02-23 11:29:07.004+00 2024-05-19 11:29:07.004+00 -47 f PixelPirate pixelpirate@gmail.com \N **** \N default t t f \N \N 2023-09-30 11:29:07.006+00 2023-10-02 11:29:07.006+00 -48 f ElectronEagle electroneagle@hackalot.nl \N **** \N default t t f \N \N 2023-03-19 11:29:07.007+00 2023-06-10 11:29:07.007+00 -49 t CircuitSavant circuitsavant@hackalot.nl \N **** \N default f t f \N \N 2024-02-08 11:29:07.009+00 2024-05-16 11:29:07.009+00 -50 f GameGladiator gamegladiator@techinc.nl \N **** \N default t t f \N \N 2023-07-09 11:29:07.011+00 2023-09-15 11:29:07.011+00 -51 f ByteBlitz byteblitz@techinc.nl \N **** \N default t t f \N \N 2024-03-18 11:29:07.013+00 2024-04-19 11:29:07.013+00 -52 f CyberSavvy cybersavvy@bitlair.nl \N **** \N default t f f \N \N 2023-09-06 11:29:07.015+00 2023-11-15 11:29:07.015+00 -53 f CodeCraftsman codecraftsman@techinc.nl \N **** \N default t t f \N \N 2023-03-02 11:29:07.017+00 2023-06-01 11:29:07.017+00 -54 f PixelPro pixelpro@hotmail.com \N **** \N default t t f \N \N 2023-05-21 11:29:07.018+00 2023-06-05 11:29:07.018+00 -55 f LogicLoreMaster logicloremaster@hackalot.nl \N **** \N default t f f \N \N 2023-03-24 11:29:07.02+00 2023-05-10 11:29:07.02+00 -56 f ElectronEmperor electronemperor@techinc.nl \N **** \N default t t f \N \N 2023-05-11 11:29:07.022+00 2023-08-14 11:29:07.022+00 -57 f CircuitChamp circuitchamp@gmail.com \N **** \N default t t f \N \N 2023-10-21 11:29:07.024+00 2024-01-03 11:29:07.024+00 -58 f GameGizmo gamegizmo@hotmail.com \N **** \N default t t f \N \N 2023-06-04 11:29:07.026+00 2023-08-24 11:29:07.026+00 -59 f ByteBrawler bytebrawler@techinc.nl \N **** \N default t t f \N \N 2023-12-20 11:29:07.028+00 2024-01-23 11:29:07.028+00 -60 f TechTrailblazer techtrailblazer@gmail.com \N **** \N default t t f \N \N 2023-10-25 11:29:07.029+00 2023-11-19 11:29:07.029+00 -61 f CodeCaptain codecaptain@techinc.nl \N **** \N default t t f \N \N 2023-01-03 11:29:07.031+00 2023-01-06 11:29:07.031+00 -62 f PixelPathfinder pixelpathfinder@gmail.com \N **** \N default t t f \N \N 2023-03-07 11:29:07.033+00 2023-03-26 11:29:07.033+00 -63 f LogicLionheart logiclionheart@techinc.nl \N **** \N default t t f \N \N 2024-01-02 11:29:07.035+00 2024-03-31 11:29:07.035+00 -64 f ElectronExpedition electronexpedition@hack42.nl \N **** \N default t t f \N \N 2023-10-20 11:29:07.036+00 2023-11-30 11:29:07.036+00 -65 f NanoNoble nanonoble@gmail.com \N **** \N default t t f \N \N 2022-11-14 11:29:07.037+00 2023-01-12 11:29:07.038+00 -66 f CircuitCommander circuitcommander@bitlair.nl \N **** \N default t t f \N \N 2023-01-26 11:29:07.039+00 2023-02-28 11:29:07.039+00 -67 f GameGlobetrotter gameglobetrotter@bitlair.nl \N **** \N default t t f \N \N 2023-01-05 11:29:07.04+00 2023-02-16 11:29:07.04+00 -68 f CyberSherpa cybersherpa@hack42.nl \N **** \N default f t f \N \N 2023-03-19 11:29:07.042+00 2023-04-24 11:29:07.042+00 -69 f CyberCraftsman cybercraftsman@bitlair.nl \N **** \N default t f f \N \N 2024-04-26 11:29:07.044+00 2024-05-08 11:29:07.044+00 -70 f CodeConnoisseur codeconnoisseur@hotmail.com \N **** \N default t t f \N \N 2023-01-22 11:29:07.047+00 2023-04-20 11:29:07.047+00 +COPY badgehub.users (id, email, admin, name, password, remember_token, editor, public, show_projects, email_verified_at, created_at, updated_at, deleted_at) FROM stdin; +0 techtinkerer@hotmail.com f TechTinkerer **** \N default t t \N 2023-06-01 11:29:06.891+00 2023-08-19 11:29:06.9+00 \N +1 codecrafter@hotmail.com f CodeCrafter **** \N default t t \N 2023-12-19 11:29:06.912+00 2024-01-07 11:29:06.913+00 \N +2 pixelpilot@gmail.com f PixelPilot **** \N default t t \N 2022-11-29 11:29:06.917+00 2023-01-17 11:29:06.918+00 \N +3 logiclion@techinc.nl f LogicLion **** \N default t t \N 2024-02-14 11:29:06.92+00 2024-03-20 11:29:06.921+00 \N +4 electroneager@hackalot.nl f ElectronEager **** \N default t t \N 2024-02-08 11:29:06.923+00 2024-02-26 11:29:06.924+00 \N +5 nanonomad@hack42.nl f NanoNomad **** \N default f t \N 2024-05-13 11:29:06.927+00 2024-06-13 11:29:06.927+00 \N +6 circuitcraze@hotmail.com t CircuitCraze **** \N default t f \N 2024-01-27 11:29:06.931+00 2024-03-14 11:29:06.932+00 \N +7 gameglider@techinc.nl f GameGlider **** \N default t t \N 2023-09-14 11:29:06.934+00 2023-11-27 11:29:06.934+00 \N +8 byteblast@hotmail.com f ByteBlast **** \N default t f \N 2022-11-20 11:29:06.937+00 2022-12-05 11:29:06.937+00 \N +9 cybercraft@hotmail.com f CyberCraft **** \N default t t \N 2023-03-11 11:29:06.94+00 2023-05-14 11:29:06.941+00 \N +10 digitaldynamo@hackalot.nl f DigitalDynamo **** \N default t t \N 2024-04-15 11:29:06.944+00 2024-04-22 11:29:06.944+00 \N +11 codecreator@hotmail.com f CodeCreator **** \N default t f \N 2023-10-12 11:29:06.946+00 2023-12-11 11:29:06.946+00 \N +12 pixelpulse@gmail.com f PixelPulse **** \N default f f \N 2024-03-27 11:29:06.948+00 2024-05-08 11:29:06.948+00 \N +13 logicluminary@hack42.nl t LogicLuminary **** \N default t t \N 2022-12-18 11:29:06.949+00 2023-02-16 11:29:06.949+00 \N +14 electronecho@gmail.com f ElectronEcho **** \N default t t \N 2024-03-08 11:29:06.951+00 2024-06-04 11:29:06.951+00 \N +15 nanoninja@bitlair.nl f NanoNinja **** \N default f t \N 2023-03-29 11:29:06.953+00 2023-06-26 11:29:06.953+00 \N +16 circuitchampion@gmail.com f CircuitChampion **** \N default t t \N 2024-01-05 11:29:06.955+00 2024-01-19 11:29:06.955+00 \N +17 gamegazer@hackalot.nl t GameGazer **** \N default t t \N 2022-11-23 11:29:06.956+00 2023-02-08 11:29:06.956+00 \N +18 bytebuddy@hack42.nl f ByteBuddy **** \N default t t \N 2022-12-08 11:29:06.957+00 2023-01-28 11:29:06.957+00 \N +19 techtornado@techinc.nl f TechTornado **** \N default t t \N 2023-08-20 11:29:06.959+00 2023-11-27 11:29:06.959+00 \N +20 codechampion@hackalot.nl f CodeChampion **** \N default t t \N 2022-11-10 11:29:06.96+00 2022-11-20 11:29:06.961+00 \N +21 pixelprodigy@techinc.nl t PixelProdigy **** \N default t t \N 2023-11-01 11:29:06.962+00 2023-12-06 11:29:06.962+00 \N +22 logiclabyrinth@techinc.nl f LogicLabyrinth **** \N default t t \N 2024-05-21 11:29:06.963+00 2024-06-04 11:29:06.964+00 \N +23 electronexplorer@bitlair.nl f ElectronExplorer **** \N default t t \N 2023-04-01 11:29:06.965+00 2023-05-25 11:29:06.965+00 \N +24 nanonavigator@gmail.com f NanoNavigator **** \N default t t \N 2022-10-02 11:29:06.967+00 2022-10-04 11:29:06.967+00 \N +25 circuitcatalyst@bitlair.nl f CircuitCatalyst **** \N default f t \N 2023-05-16 11:29:06.968+00 2023-08-13 11:29:06.968+00 \N +26 gameguru@hackalot.nl f GameGuru **** \N default t t \N 2024-05-18 11:29:06.97+00 2024-07-15 11:29:06.97+00 \N +27 byteblaze@gmail.com f ByteBlaze **** \N default t t \N 2023-12-29 11:29:06.971+00 2024-01-22 11:29:06.971+00 \N +28 digitaldreamer@bitlair.nl f DigitalDreamer **** \N default t t \N 2024-03-24 11:29:06.973+00 2024-04-08 11:29:06.973+00 \N +29 codecommander@hotmail.com f CodeCommander **** \N default t t \N 2022-11-16 11:29:06.974+00 2023-01-05 11:29:06.974+00 \N +30 pixelpioneer@bitlair.nl f PixelPioneer **** \N default f t \N 2023-08-09 11:29:06.976+00 2023-11-06 11:29:06.976+00 \N +31 logiclegend@hackalot.nl f LogicLegend **** \N default t t \N 2024-01-07 11:29:06.977+00 2024-03-14 11:29:06.977+00 \N +32 electronelite@hotmail.com f ElectronElite **** \N default t t \N 2023-12-30 11:29:06.978+00 2024-01-07 11:29:06.978+00 \N +33 nanonerd@gmail.com f NanoNerd **** \N default t t \N 2023-11-04 11:29:06.98+00 2023-11-04 11:29:06.98+00 \N +34 circuitcaptain@hack42.nl f CircuitCaptain **** \N default t t \N 2024-01-19 11:29:06.981+00 2024-04-12 11:29:06.981+00 \N +35 gamegenius@gmail.com f GameGenius **** \N default t f \N 2023-06-11 11:29:06.983+00 2023-09-14 11:29:06.983+00 \N +36 bytebolt@techinc.nl f ByteBolt **** \N default t t \N 2023-12-11 11:29:06.984+00 2024-01-14 11:29:06.984+00 \N +37 cybercipher@hackalot.nl f CyberCipher **** \N default t t \N 2022-10-01 11:29:06.986+00 2022-10-05 11:29:06.986+00 \N +38 codeconqueror@hack42.nl f CodeConqueror **** \N default f t \N 2023-09-26 11:29:06.988+00 2023-10-18 11:29:06.988+00 \N +39 pixelpaladin@hack42.nl f PixelPaladin **** \N default t t \N 2022-11-02 11:29:06.989+00 2022-12-24 11:29:06.99+00 \N +40 logiclore@techinc.nl f LogicLore **** \N default t t \N 2024-04-15 11:29:06.992+00 2024-07-01 11:29:06.992+00 \N +41 electronenigma@gmail.com f ElectronEnigma **** \N default t t \N 2022-10-01 11:29:06.993+00 2022-10-24 11:29:06.993+00 \N +42 circuitconnoisseur@gmail.com f CircuitConnoisseur **** \N default f t \N 2022-10-26 11:29:06.996+00 2022-12-16 11:29:06.996+00 \N +43 gameguardian@techinc.nl f GameGuardian **** \N default t t \N 2023-07-16 11:29:06.998+00 2023-10-11 11:29:06.998+00 \N +44 bytebandit@gmail.com f ByteBandit **** \N default t t \N 2023-01-08 11:29:07+00 2023-01-15 11:29:07+00 \N +45 techtinker@techinc.nl t TechTinker **** \N default t t \N 2023-10-05 11:29:07.002+00 2023-11-17 11:29:07.002+00 \N +46 codecrusader@hotmail.com f CodeCrusader **** \N default t t \N 2024-02-23 11:29:07.004+00 2024-05-19 11:29:07.004+00 \N +47 pixelpirate@gmail.com f PixelPirate **** \N default t t \N 2023-09-30 11:29:07.006+00 2023-10-02 11:29:07.006+00 \N +48 electroneagle@hackalot.nl f ElectronEagle **** \N default t t \N 2023-03-19 11:29:07.007+00 2023-06-10 11:29:07.007+00 \N +49 circuitsavant@hackalot.nl t CircuitSavant **** \N default f t \N 2024-02-08 11:29:07.009+00 2024-05-16 11:29:07.009+00 \N +50 gamegladiator@techinc.nl f GameGladiator **** \N default t t \N 2023-07-09 11:29:07.011+00 2023-09-15 11:29:07.011+00 \N +51 byteblitz@techinc.nl f ByteBlitz **** \N default t t \N 2024-03-18 11:29:07.013+00 2024-04-19 11:29:07.013+00 \N +52 cybersavvy@bitlair.nl f CyberSavvy **** \N default t f \N 2023-09-06 11:29:07.015+00 2023-11-15 11:29:07.015+00 \N +53 codecraftsman@techinc.nl f CodeCraftsman **** \N default t t \N 2023-03-02 11:29:07.017+00 2023-06-01 11:29:07.017+00 \N +54 pixelpro@hotmail.com f PixelPro **** \N default t t \N 2023-05-21 11:29:07.018+00 2023-06-05 11:29:07.018+00 \N +55 logicloremaster@hackalot.nl f LogicLoreMaster **** \N default t f \N 2023-03-24 11:29:07.02+00 2023-05-10 11:29:07.02+00 \N +56 electronemperor@techinc.nl f ElectronEmperor **** \N default t t \N 2023-05-11 11:29:07.022+00 2023-08-14 11:29:07.022+00 \N +57 circuitchamp@gmail.com f CircuitChamp **** \N default t t \N 2023-10-21 11:29:07.024+00 2024-01-03 11:29:07.024+00 \N +58 gamegizmo@hotmail.com f GameGizmo **** \N default t t \N 2023-06-04 11:29:07.026+00 2023-08-24 11:29:07.026+00 \N +59 bytebrawler@techinc.nl f ByteBrawler **** \N default t t \N 2023-12-20 11:29:07.028+00 2024-01-23 11:29:07.028+00 \N +60 techtrailblazer@gmail.com f TechTrailblazer **** \N default t t \N 2023-10-25 11:29:07.029+00 2023-11-19 11:29:07.029+00 \N +61 codecaptain@techinc.nl f CodeCaptain **** \N default t t \N 2023-01-03 11:29:07.031+00 2023-01-06 11:29:07.031+00 \N +62 pixelpathfinder@gmail.com f PixelPathfinder **** \N default t t \N 2023-03-07 11:29:07.033+00 2023-03-26 11:29:07.033+00 \N +63 logiclionheart@techinc.nl f LogicLionheart **** \N default t t \N 2024-01-02 11:29:07.035+00 2024-03-31 11:29:07.035+00 \N +64 electronexpedition@hack42.nl f ElectronExpedition **** \N default t t \N 2023-10-20 11:29:07.036+00 2023-11-30 11:29:07.036+00 \N +65 nanonoble@gmail.com f NanoNoble **** \N default t t \N 2022-11-14 11:29:07.037+00 2023-01-12 11:29:07.038+00 \N +66 circuitcommander@bitlair.nl f CircuitCommander **** \N default t t \N 2023-01-26 11:29:07.039+00 2023-02-28 11:29:07.039+00 \N +67 gameglobetrotter@bitlair.nl f GameGlobetrotter **** \N default t t \N 2023-01-05 11:29:07.04+00 2023-02-16 11:29:07.04+00 \N +68 cybersherpa@hack42.nl f CyberSherpa **** \N default f t \N 2023-03-19 11:29:07.042+00 2023-04-24 11:29:07.042+00 \N +69 cybercraftsman@bitlair.nl f CyberCraftsman **** \N default t f \N 2024-04-26 11:29:07.044+00 2024-05-08 11:29:07.044+00 \N +70 codeconnoisseur@hotmail.com f CodeConnoisseur **** \N default t t \N 2023-01-22 11:29:07.047+00 2023-04-20 11:29:07.047+00 \N +\. + + +-- +-- Data for Name: versioned_dependencies; Type: TABLE DATA; Schema: badgehub; Owner: badgehub +-- + +COPY badgehub.versioned_dependencies (id, project_slug, depends_on_project_slug, semantic_version_range, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: versions; Type: TABLE DATA; Schema: badgehub; Owner: badgehub +-- + +COPY badgehub.versions (id, project_slug, app_metadata_json_id, revision, semantic_version, zip, size_of_zip, git_commit_id, published_at, download_count, created_at, updated_at, deleted_at) FROM stdin; +1 codecraft 1 0 \N \N \N \N \N 0 2024-01-05 11:29:07.05+00 2024-03-20 11:29:07.05+00 \N +2 pixelpulse 2 0 \N \N \N \N \N 0 2023-08-30 11:29:07.053+00 2023-10-29 11:29:07.053+00 \N +3 bitblast 3 0 \N \N \N \N \N 0 2023-09-22 11:29:07.055+00 2023-11-13 11:29:07.055+00 \N +4 nanogames 4 0 \N \N \N \N \N 0 2022-10-24 11:29:07.056+00 2023-01-02 11:29:07.057+00 \N +5 electraplay 5 0 \N \N \N \N \N 0 2023-09-04 11:29:07.058+00 2023-10-06 11:29:07.058+00 \N +6 circuitforge 6 0 \N \N \N \N \N 0 2023-01-28 11:29:07.06+00 2023-04-24 11:29:07.06+00 \N +7 bytebash 7 0 \N \N \N \N \N 0 2023-08-02 11:29:07.061+00 2023-10-27 11:29:07.061+00 \N +8 codecanvas 8 0 \N \N \N \N \N 0 2023-12-31 11:29:07.063+00 2024-02-09 11:29:07.063+00 \N +9 sparkscript 9 0 \N \N \N \N \N 0 2022-12-13 11:29:07.064+00 2023-01-13 11:29:07.064+00 \N +10 logicland 10 0 \N \N \N \N \N 0 2023-02-05 11:29:07.066+00 2023-05-13 11:29:07.066+00 \N +11 microarcade 11 0 \N \N \N \N \N 0 2024-01-05 11:29:07.069+00 2024-03-01 11:29:07.069+00 \N +12 codecraze 12 0 \N \N \N \N \N 0 2024-05-07 11:29:07.071+00 2024-07-22 11:29:07.071+00 \N +13 gamegenius 13 0 \N \N \N \N \N 0 2023-12-14 11:29:07.073+00 2024-01-22 11:29:07.073+00 \N +14 pixelpal 14 0 \N \N \N \N \N 0 2023-08-10 11:29:07.075+00 2023-10-23 11:29:07.075+00 \N +15 electronica 15 0 \N \N \N \N \N 0 2024-02-26 11:29:07.078+00 2024-04-29 11:29:07.078+00 \N +16 codequest 16 0 \N \N \N \N \N 0 2023-05-15 11:29:07.08+00 2023-07-04 11:29:07.08+00 \N +17 circuitcraft 17 0 \N \N \N \N \N 0 2023-06-19 11:29:07.082+00 2023-07-20 11:29:07.083+00 \N +18 bytebeat 18 0 \N \N \N \N \N 0 2023-06-07 11:29:07.084+00 2023-07-30 11:29:07.084+00 \N +19 nanonexus 19 0 \N \N \N \N \N 0 2023-06-28 11:29:07.087+00 2023-07-16 11:29:07.087+00 \N +20 bitbox 20 0 \N \N \N \N \N 0 2023-10-15 11:29:07.089+00 2023-10-21 11:29:07.089+00 \N +21 circuitchaos 21 0 \N \N \N \N \N 0 2023-02-08 11:29:07.09+00 2023-03-11 11:29:07.09+00 \N +22 codecrafter 22 0 \N \N \N \N \N 0 2024-04-16 11:29:07.092+00 2024-04-28 11:29:07.092+00 \N +23 pixelpioneer 23 0 \N \N \N \N \N 0 2022-12-07 11:29:07.094+00 2023-02-18 11:29:07.094+00 \N +24 logiclab 24 0 \N \N \N \N \N 0 2023-05-14 11:29:07.096+00 2023-08-16 11:29:07.096+00 \N +25 byteblitz 25 0 \N \N \N \N \N 0 2024-03-29 11:29:07.099+00 2024-05-23 11:29:07.099+00 \N +26 codewave 26 0 \N \N \N \N \N 0 2024-03-09 11:29:07.1+00 2024-06-07 11:29:07.1+00 \N +27 nanonet 27 0 \N \N \N \N \N 0 2023-11-16 11:29:07.102+00 2024-01-16 11:29:07.102+00 \N +28 electraforge 28 0 \N \N \N \N \N 0 2023-04-21 11:29:07.103+00 2023-06-29 11:29:07.104+00 \N +29 gamegrid 29 0 \N \N \N \N \N 0 2022-10-22 11:29:07.105+00 2022-12-09 11:29:07.105+00 \N +30 logicloom 30 0 \N \N \N \N \N 0 2023-08-28 11:29:07.106+00 2023-10-03 11:29:07.106+00 \N +31 pixelplaza 31 0 \N \N \N \N \N 0 2023-06-27 11:29:07.108+00 2023-06-28 11:29:07.108+00 \N +32 codecity 32 0 \N \N \N \N \N 0 2022-10-01 11:29:07.11+00 2022-10-27 11:29:07.11+00 \N +33 nanoarcade 33 0 \N \N \N \N \N 0 2023-07-24 11:29:07.112+00 2023-08-11 11:29:07.112+00 \N +34 electronera 34 0 \N \N \N \N \N 0 2023-11-12 11:29:07.114+00 2024-01-17 11:29:07.114+00 \N +35 bitbazaar 35 0 \N \N \N \N \N 0 2022-12-12 11:29:07.115+00 2023-02-13 11:29:07.115+00 \N +36 logiclegends 36 0 \N \N \N \N \N 0 2023-03-16 11:29:07.116+00 2023-05-22 11:29:07.116+00 \N +37 codeclan 37 0 \N \N \N \N \N 0 2023-11-28 11:29:07.118+00 2024-01-06 11:29:07.118+00 \N +38 pixelportal 38 0 \N \N \N \N \N 0 2024-02-12 11:29:07.119+00 2024-04-25 11:29:07.119+00 \N +39 circuitcraze 39 0 \N \N \N \N \N 0 2022-11-04 11:29:07.12+00 2023-01-22 11:29:07.12+00 \N +40 bytebuster 40 0 \N \N \N \N \N 0 2022-12-09 11:29:07.122+00 2022-12-24 11:29:07.122+00 \N +41 nanonovel 41 0 \N \N \N \N \N 0 2024-03-17 11:29:07.123+00 2024-05-14 11:29:07.123+00 \N +42 electraeden 42 0 \N \N \N \N \N 0 2023-05-19 11:29:07.125+00 2023-06-03 11:29:07.125+00 \N +43 codecomet 43 0 \N \N \N \N \N 0 2024-02-25 11:29:07.126+00 2024-03-05 11:29:07.126+00 \N +44 pixelplayground 44 0 \N \N \N \N \N 0 2023-02-15 11:29:07.128+00 2023-05-12 11:29:07.128+00 \N +45 logiclandia 45 0 \N \N \N \N \N 0 2023-08-10 11:29:07.129+00 2023-09-23 11:29:07.129+00 \N +46 bytebounce 46 0 \N \N \N \N \N 0 2024-03-02 11:29:07.13+00 2024-05-15 11:29:07.13+00 \N +47 circuitcarnival 47 0 \N \N \N \N \N 0 2023-02-03 11:29:07.131+00 2023-03-10 11:29:07.131+00 \N +48 codecove 48 0 \N \N \N \N \N 0 2022-10-15 11:29:07.132+00 2022-12-05 11:29:07.132+00 \N +49 nanonest 49 0 \N \N \N \N \N 0 2023-10-05 11:29:07.133+00 2023-11-12 11:29:07.134+00 \N +50 electraentertain 50 0 \N \N \N \N \N 0 2023-07-11 11:29:07.135+00 2023-09-21 11:29:07.135+00 \N +51 gamegalaxy 51 0 \N \N \N \N \N 0 2024-02-02 11:29:07.136+00 2024-05-05 11:29:07.136+00 \N +52 logiclabyrinth 52 0 \N \N \N \N \N 0 2024-01-04 11:29:07.138+00 2024-01-04 11:29:07.138+00 \N +53 byteblaster 53 0 \N \N \N \N \N 0 2022-10-12 11:29:07.14+00 2022-11-07 11:29:07.14+00 \N +54 codecompass 54 0 \N \N \N \N \N 0 2023-02-18 11:29:07.142+00 2023-03-20 11:29:07.142+00 \N +55 nanonation 55 0 \N \N \N \N \N 0 2024-03-06 11:29:07.143+00 2024-06-11 11:29:07.143+00 \N +56 electraempire 56 0 \N \N \N \N \N 0 2023-04-17 11:29:07.144+00 2023-06-14 11:29:07.145+00 \N +57 gamegarden 57 0 \N \N \N \N \N 0 2024-04-04 11:29:07.146+00 2024-04-29 11:29:07.146+00 \N +58 pixelpeak 58 0 \N \N \N \N \N 0 2023-06-13 11:29:07.147+00 2023-07-13 11:29:07.147+00 \N +59 circuitcelestial 59 0 \N \N \N \N \N 0 2023-07-14 11:29:07.149+00 2023-07-19 11:29:07.149+00 \N +60 codecrusade 60 0 \N \N \N \N \N 0 2023-10-09 11:29:07.15+00 2023-12-23 11:29:07.15+00 \N +61 nanonebula 61 0 \N \N \N \N \N 0 2023-03-03 11:29:07.151+00 2023-05-22 11:29:07.151+00 \N +62 electraenclave 62 0 \N \N \N \N \N 0 2023-09-18 11:29:07.153+00 2023-10-07 11:29:07.153+00 \N +63 gamegizmo 63 0 \N \N \N \N \N 0 2023-12-04 11:29:07.155+00 2024-01-17 11:29:07.155+00 \N +64 pixelplanet 64 0 \N \N \N \N \N 0 2023-02-04 11:29:07.156+00 2023-04-06 11:29:07.156+00 \N +65 logiclounge 65 0 \N \N \N \N \N 0 2022-12-24 11:29:07.157+00 2023-03-02 11:29:07.157+00 \N +66 bytebeacon 66 0 \N \N \N \N \N 0 2024-01-25 11:29:07.158+00 2024-02-04 11:29:07.158+00 \N +67 codecircus 67 0 \N \N \N \N \N 0 2023-07-04 11:29:07.16+00 2023-08-14 11:29:07.16+00 \N +68 nanonook 68 0 \N \N \N \N \N 0 2022-12-14 11:29:07.161+00 2023-03-12 11:29:07.161+00 \N +69 electraelysium 69 0 \N \N \N \N \N 0 2023-06-25 11:29:07.163+00 2023-07-28 11:29:07.163+00 \N +70 gameglimpse 70 0 \N \N \N \N \N 0 2023-01-24 11:29:07.164+00 2023-03-12 11:29:07.164+00 \N +71 pixelparadise 71 0 \N \N \N \N \N 0 2023-03-02 11:29:07.165+00 2023-03-22 11:29:07.165+00 \N +72 codecoast 72 0 \N \N \N \N \N 0 2024-01-18 11:29:07.167+00 2024-02-04 11:29:07.167+00 \N +73 nanonirvana 73 0 \N \N \N \N \N 0 2023-06-18 11:29:07.168+00 2023-07-18 11:29:07.168+00 \N +74 electraedifice 74 0 \N \N \N \N \N 0 2022-11-15 11:29:07.17+00 2022-11-25 11:29:07.17+00 \N +75 gamegen 75 0 \N \N \N \N \N 0 2023-01-16 11:29:07.171+00 2023-03-02 11:29:07.171+00 \N +76 pixelpandemonium 76 0 \N \N \N \N \N 0 2023-12-21 11:29:07.172+00 2024-03-23 11:29:07.172+00 \N +77 logiclagoon 77 0 \N \N \N \N \N 0 2023-11-25 11:29:07.174+00 2023-12-13 11:29:07.174+00 \N +78 byteblaze 78 0 \N \N \N \N \N 0 2023-03-20 11:29:07.175+00 2023-04-05 11:29:07.175+00 \N +79 codecorridor 79 0 \N \N \N \N \N 0 2023-05-18 11:29:07.176+00 2023-07-21 11:29:07.176+00 \N +80 hacksimulator 80 0 \N \N \N \N \N 0 2023-01-31 11:29:07.177+00 2023-03-06 11:29:07.177+00 \N +81 codecrunch 81 0 \N \N \N \N \N 0 2023-02-21 11:29:07.178+00 2023-05-27 11:29:07.178+00 \N +82 securecraft 82 0 \N \N \N \N \N 0 2024-01-29 11:29:07.179+00 2024-03-16 11:29:07.179+00 \N +83 cryptopulse 83 0 \N \N \N \N \N 0 2023-01-18 11:29:07.181+00 2023-04-16 11:29:07.181+00 \N +84 dataforge 84 0 \N \N \N \N \N 0 2023-07-15 11:29:07.182+00 2023-07-24 11:29:07.182+00 \N +85 cipherquest 85 0 \N \N \N \N \N 0 2022-12-14 11:29:07.183+00 2022-12-24 11:29:07.183+00 \N +86 hackquest 86 0 \N \N \N \N \N 0 2023-08-16 11:29:07.185+00 2023-08-18 11:29:07.185+00 \N +87 securesphere 87 0 \N \N \N \N \N 0 2023-12-26 11:29:07.186+00 2024-03-15 11:29:07.186+00 \N \. -- --- Name: badge_project_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub +-- Name: app_metadata_jsons_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub -- -SELECT pg_catalog.setval('badgehub.badge_project_id_seq', 660, true); +SELECT pg_catalog.setval('badgehub.app_metadata_jsons_id_seq', 87, true); -- --- Name: badges_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub +-- Name: migrations_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub -- -SELECT pg_catalog.setval('badgehub.badges_id_seq', 5, true); +SELECT pg_catalog.setval('badgehub.migrations_id_seq', 1, true); -- --- Name: categories_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub +-- Name: project_statuses_on_badges_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub -- -SELECT pg_catalog.setval('badgehub.categories_id_seq', 15, true); +SELECT pg_catalog.setval('badgehub.project_statuses_on_badges_id_seq', 105, true); -- --- Name: projects_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub +-- Name: versioned_dependencies_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub -- -SELECT pg_catalog.setval('badgehub.projects_id_seq', 189, true); +SELECT pg_catalog.setval('badgehub.versioned_dependencies_id_seq', 1, false); -- --- Name: users_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub +-- Name: versions_id_seq; Type: SEQUENCE SET; Schema: badgehub; Owner: badgehub -- -SELECT pg_catalog.setval('badgehub.users_id_seq', 1, true); +SELECT pg_catalog.setval('badgehub.versions_id_seq', 87, true); -- --- Name: badge_project idx_24600_primary; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: app_metadata_jsons app_metadata_jsons_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.badge_project - ADD CONSTRAINT idx_24600_primary PRIMARY KEY (id); +ALTER TABLE ONLY badgehub.app_metadata_jsons + ADD CONSTRAINT app_metadata_jsons_pkey PRIMARY KEY (id); -- --- Name: badges idx_24606_primary; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: badges badges_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- ALTER TABLE ONLY badgehub.badges - ADD CONSTRAINT idx_24606_primary PRIMARY KEY (id); + ADD CONSTRAINT badges_pkey PRIMARY KEY (slug); -- --- Name: categories idx_24613_primary; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: categories categories_name_key; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- ALTER TABLE ONLY badgehub.categories - ADD CONSTRAINT idx_24613_primary PRIMARY KEY (id); + ADD CONSTRAINT categories_name_key UNIQUE (name); -- --- Name: projects idx_24649_primary; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: categories categories_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.categories + ADD CONSTRAINT categories_pkey PRIMARY KEY (slug); + + +-- +-- Name: project_statuses_on_badges project_statuses_on_badges_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.project_statuses_on_badges + ADD CONSTRAINT project_statuses_on_badges_pkey PRIMARY KEY (id); + + +-- +-- Name: projects projects_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- ALTER TABLE ONLY badgehub.projects - ADD CONSTRAINT idx_24649_primary PRIMARY KEY (id); + ADD CONSTRAINT projects_pkey PRIMARY KEY (slug); + + +-- +-- Name: users users_email_key; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.users + ADD CONSTRAINT users_email_key UNIQUE (email); -- --- Name: users idx_24661_primary; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: users users_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- ALTER TABLE ONLY badgehub.users - ADD CONSTRAINT idx_24661_primary PRIMARY KEY (id); + ADD CONSTRAINT users_pkey PRIMARY KEY (id); -- --- Name: idx_24600_badge_project_badge_id_foreign; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: versioned_dependencies versioned_dependencies_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- -CREATE INDEX idx_24600_badge_project_badge_id_foreign ON badgehub.badge_project USING btree (badge_id); +ALTER TABLE ONLY badgehub.versioned_dependencies + ADD CONSTRAINT versioned_dependencies_pkey PRIMARY KEY (id); -- --- Name: idx_24600_badge_project_project_id_foreign; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: versions versions_pkey; Type: CONSTRAINT; Schema: badgehub; Owner: badgehub -- -CREATE INDEX idx_24600_badge_project_project_id_foreign ON badgehub.badge_project USING btree (project_id); +ALTER TABLE ONLY badgehub.versions + ADD CONSTRAINT versions_pkey PRIMARY KEY (id); -- --- Name: idx_24606_badges_slug_unique; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_app_metadata_jsons_category; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE UNIQUE INDEX idx_24606_badges_slug_unique ON badgehub.badges USING btree (slug); +CREATE INDEX idx_app_metadata_jsons_category ON badgehub.app_metadata_jsons USING btree (category); -- --- Name: idx_24613_categories_name_unique; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_app_metadata_jsons_is_hidden; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE UNIQUE INDEX idx_24613_categories_name_unique ON badgehub.categories USING btree (name); +CREATE INDEX idx_app_metadata_jsons_is_hidden ON badgehub.app_metadata_jsons USING btree (is_hidden); -- --- Name: idx_24618_dependencies_depends_on_project_id_foreign; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_app_metadata_jsons_name; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE INDEX idx_24618_dependencies_depends_on_project_id_foreign ON badgehub.dependencies USING btree (depends_on_project_id); +CREATE INDEX idx_app_metadata_jsons_name ON badgehub.app_metadata_jsons USING btree (name); -- --- Name: idx_24618_dependencies_project_id_foreign; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_categories_deleted_at; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE INDEX idx_24618_dependencies_project_id_foreign ON badgehub.dependencies USING btree (project_id); +CREATE INDEX idx_categories_deleted_at ON badgehub.categories USING btree (deleted_at); -- --- Name: idx_24649_projects_name_unique; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_categories_name; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE UNIQUE INDEX idx_24649_projects_name_unique ON badgehub.projects USING btree (name); +CREATE INDEX idx_categories_name ON badgehub.categories USING btree (name); -- --- Name: idx_24649_projects_slug_unique; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_user_id; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE UNIQUE INDEX idx_24649_projects_slug_unique ON badgehub.projects USING btree (slug); +CREATE INDEX idx_user_id ON badgehub.projects USING btree (user_id); -- --- Name: idx_24649_projects_user_id_foreign; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_versions_project_slug; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE INDEX idx_24649_projects_user_id_foreign ON badgehub.projects USING btree (user_id); +CREATE INDEX idx_versions_project_slug ON badgehub.versions USING btree (project_slug); -- --- Name: idx_24661_users_email_unique; Type: INDEX; Schema: badgehub; Owner: badgehub +-- Name: idx_versions_published_at; Type: INDEX; Schema: badgehub; Owner: badgehub -- -CREATE UNIQUE INDEX idx_24661_users_email_unique ON badgehub.users USING btree (email); +CREATE INDEX idx_versions_published_at ON badgehub.versions USING btree (published_at); -- --- Name: badge_project badge_project_badge_id_foreign; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: app_metadata_jsons app_metadata_jsons_category_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.badge_project - ADD CONSTRAINT badge_project_badge_id_foreign FOREIGN KEY (badge_id) REFERENCES badgehub.badges(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY badgehub.app_metadata_jsons + ADD CONSTRAINT app_metadata_jsons_category_fk FOREIGN KEY (category) REFERENCES badgehub.categories(name) ON DELETE SET DEFAULT; + + +-- +-- Name: project_statuses_on_badges project_statuses_on_badges_badge_slug_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.project_statuses_on_badges + ADD CONSTRAINT project_statuses_on_badges_badge_slug_fk FOREIGN KEY (badge_slug) REFERENCES badgehub.badges(slug) ON DELETE CASCADE; + + +-- +-- Name: project_statuses_on_badges project_statuses_on_badges_project_slug_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.project_statuses_on_badges + ADD CONSTRAINT project_statuses_on_badges_project_slug_fk FOREIGN KEY (project_slug) REFERENCES badgehub.projects(slug) ON DELETE CASCADE; + + +-- +-- Name: projects projects_user_id_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.projects + ADD CONSTRAINT projects_user_id_fk FOREIGN KEY (user_id) REFERENCES badgehub.users(id) ON DELETE CASCADE; + + +-- +-- Name: projects projects_version_id_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- + +ALTER TABLE ONLY badgehub.projects + ADD CONSTRAINT projects_version_id_fk FOREIGN KEY (version_id) REFERENCES badgehub.versions(id) ON DELETE SET NULL; -- --- Name: badge_project badge_project_project_id_foreign; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: versioned_dependencies versioned_dependency_project_slug_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.badge_project - ADD CONSTRAINT badge_project_project_id_foreign FOREIGN KEY (project_id) REFERENCES badgehub.projects(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY badgehub.versioned_dependencies + ADD CONSTRAINT versioned_dependency_project_slug_fk FOREIGN KEY (project_slug) REFERENCES badgehub.projects(slug) ON DELETE CASCADE; -- --- Name: dependencies dependencies_depends_on_project_id_foreign; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: versions versions_app_metadata_json_id_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.dependencies - ADD CONSTRAINT dependencies_depends_on_project_id_foreign FOREIGN KEY (depends_on_project_id) REFERENCES badgehub.projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY badgehub.versions + ADD CONSTRAINT versions_app_metadata_json_id_fk FOREIGN KEY (app_metadata_json_id) REFERENCES badgehub.app_metadata_jsons(id) ON DELETE CASCADE; -- --- Name: dependencies dependencies_project_id_foreign; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub +-- Name: versions versions_project_slug_fk; Type: FK CONSTRAINT; Schema: badgehub; Owner: badgehub -- -ALTER TABLE ONLY badgehub.dependencies - ADD CONSTRAINT dependencies_project_id_foreign FOREIGN KEY (project_id) REFERENCES badgehub.projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY badgehub.versions + ADD CONSTRAINT versions_project_slug_fk FOREIGN KEY (project_slug) REFERENCES badgehub.projects(slug) ON DELETE CASCADE; -- diff --git a/package-lock.json b/package-lock.json index 24dfe04..db4d2be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,11 @@ "dotenv": "^16.4.5", "express": "^4.21.2", "jose": "^5.9.6", + "moment": "^2.30.1", "pg": "^8.11.5", "pino-http": "^10.1.0", "pm2": "^5.4.0", + "sql-template-tag": "^5.2.1", "swagger-ui-express": "^5.0.0", "tsoa": "^6.2.1", "tsx": "^4.19.2", @@ -3961,6 +3963,14 @@ "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, "node_modules/mongodb-uri": { "version": "0.9.7", "resolved": "https://registry.npmjs.org/mongodb-uri/-/mongodb-uri-0.9.7.tgz", @@ -5409,6 +5419,14 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, + "node_modules/sql-template-tag": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/sql-template-tag/-/sql-template-tag-5.2.1.tgz", + "integrity": "sha512-lFdvXCOqWhV40A7w4oQVDyuaNFb5yO+dhsHStZzOdtDJWCBWYv4+hhATK5nPpY5v/T1OMVcLMPeN4519qIyb9Q==", + "engines": { + "node": ">=14" + } + }, "node_modules/ssh2": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.4.0.tgz", diff --git a/package.json b/package.json index edcbbfa..d4c1f53 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,11 @@ "dotenv": "^16.4.5", "express": "^4.21.2", "jose": "^5.9.6", + "moment": "^2.30.1", "pg": "^8.11.5", "pino-http": "^10.1.0", "pm2": "^5.4.0", + "sql-template-tag": "^5.2.1", "swagger-ui-express": "^5.0.0", "tsoa": "^6.2.1", "tsx": "^4.19.2", diff --git a/public/swagger.json b/public/swagger.json index 3d6a63f..a9f4a8f 100644 --- a/public/swagger.json +++ b/public/swagger.json @@ -7,8 +7,20 @@ "requestBodies": {}, "responses": {}, "schemas": { - "Device": { + "Badge": { "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, "name": { "type": "string" }, @@ -16,14 +28,34 @@ "type": "string" } }, - "required": ["name", "slug"], + "required": ["created_at", "updated_at", "name", "slug"], "type": "object", "additionalProperties": false }, + "AppCategoryName": { + "type": "string", + "enum": [ + "Uncategorised", + "Event related", + "Games", + "Graphics", + "Hardware", + "Utility", + "Wearable", + "Data", + "Silly", + "Hacking", + "Troll", + "Unusable", + "Adult", + "Virus", + "Interpreter" + ] + }, "Category": { "properties": { "name": { - "type": "string" + "$ref": "#/components/schemas/AppCategoryName" }, "slug": { "type": "string" @@ -33,57 +65,513 @@ "type": "object", "additionalProperties": false }, - "App": { + "ProjectStatusName": { + "type": "string", + "enum": ["working", "in_progress", "broken", "unknown"] + }, + "Version": { + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, + "revision": { + "type": "number", + "format": "double" + }, + "semantic_version": { + "type": "string" + }, + "zip": { + "type": "string" + }, + "size_of_zip": { + "type": "number", + "format": "double" + }, + "git_commit_id": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/components/schemas/FileMetadata" + }, + "type": "array" + }, + "app_metadata": { + "$ref": "#/components/schemas/AppMetadataJSON" + }, + "published_at": { + "type": "string", + "format": "date-time" + }, + "download_count": { + "type": "number", + "format": "double" + }, + "project_slug": { + "type": "string" + } + }, + "required": [ + "created_at", + "updated_at", + "revision", + "files", + "app_metadata", + "download_count", + "project_slug" + ], + "type": "object", + "additionalProperties": false + }, + "User": { "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string" + }, + "email": { + "type": "string" + }, + "admin": { + "type": "boolean" + }, "name": { "type": "string" }, - "slug": { + "password": { "type": "string" }, - "category_slug": { + "remember_token": { "type": "string" }, - "user_name": { + "editor": { "type": "string" + }, + "public": { + "type": "boolean" + }, + "show_projects": { + "type": "boolean" + }, + "email_verified_at": { + "type": "string", + "format": "date-time" } }, - "required": ["name", "slug", "category_slug", "user_name"], + "required": [ + "created_at", + "updated_at", + "id", + "email", + "admin", + "name", + "password", + "editor", + "public", + "show_projects" + ], "type": "object", "additionalProperties": false }, - "AppDetails": { + "FileMetadata": { "properties": { + "version": { + "$ref": "#/components/schemas/Version" + }, + "user": { + "$ref": "#/components/schemas/User" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, + "editable": { + "type": "boolean" + }, + "lintable": { + "type": "boolean" + }, + "extension": { + "type": "string" + }, + "baseName": { + "type": "string" + }, + "mime": { + "type": "string" + }, + "size_of_content": { + "type": "number", + "format": "double" + }, + "crc32": { + "type": "string" + }, + "size_formatted": { + "type": "string" + }, "name": { "type": "string" + } + }, + "required": [ + "version", + "user", + "created_at", + "updated_at", + "extension", + "baseName", + "size_of_content", + "crc32", + "size_formatted", + "name" + ], + "type": "object", + "additionalProperties": false + }, + "Record_Badge-at-slug.string_": { + "properties": {}, + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "Construct a type with a set of properties K of type T" + }, + "Record_Badge-at-slug.Array__source-string--destination-string___": { + "properties": {}, + "additionalProperties": { + "items": { + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["destination", "source"], + "type": "object" }, - "slug": { + "type": "array" + }, + "type": "object", + "description": "Construct a type with a set of properties K of type T" + }, + "AppMetadataJSON": { + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "category": { + "$ref": "#/components/schemas/AppCategoryName" + }, + "author": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "license_file": { + "type": "string" + }, + "is_library": { + "type": "boolean" + }, + "is_hidden": { + "type": "boolean" + }, + "semantic_version": { + "type": "string" + }, + "interpreter": { + "type": "string" + }, + "main_executable": { + "type": "string" + }, + "main_executable_overrides": { + "$ref": "#/components/schemas/Record_Badge-at-slug.string_" + }, + "file_mappings": { + "items": { + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["destination", "source"], + "type": "object" + }, + "type": "array" + }, + "file_mappings_overrides": { + "$ref": "#/components/schemas/Record_Badge-at-slug.Array__source-string--destination-string___" + } + }, + "type": "object", + "additionalProperties": false + }, + "ProjectSlug": { + "type": "string" + }, + "Dependency": { + "properties": { + "project_slug": { + "$ref": "#/components/schemas/ProjectSlug" + }, + "semantic_version_range": { + "type": "string" + } + }, + "required": ["project_slug", "semantic_version_range"], + "type": "object", + "additionalProperties": false + }, + "ProjectStatusOnBadge": { + "properties": { + "badge": { + "$ref": "#/components/schemas/Badge" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "$ref": "#/components/schemas/ProjectStatusName" + } + }, + "required": ["badge", "created_at", "updated_at", "status"], + "type": "object", + "additionalProperties": false + }, + "VoteFromUser": { + "properties": { + "type": { + "type": "string", + "enum": ["up", "down", "pig"] + }, + "comment": { "type": "string" }, + "user": { + "$ref": "#/components/schemas/User" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + } + }, + "required": ["type", "user", "created_at", "updated_at"], + "type": "object", + "additionalProperties": false + }, + "WarningFromUser": { + "properties": { "description": { "type": "string" }, - "category_slug": { + "user": { + "$ref": "#/components/schemas/User" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + } + }, + "required": ["description", "user", "created_at", "updated_at"], + "type": "object", + "additionalProperties": false + }, + "Project": { + "properties": { + "slug": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "git": { + "type": "string" + }, + "allow_team_fixes": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string" + }, + "min_firmware": { + "type": "number", + "format": "double" + }, + "max_firmware": { + "type": "number", + "format": "double" + }, + "git_commit_id": { + "type": "string" + }, + "published_at": { + "type": "string", + "format": "date-time" + }, + "download_counter": { + "type": "number", + "format": "double" + }, + "license": { + "type": "string" + }, + "size_of_zip": { + "type": "number", + "format": "double" + }, + "size_of_content": { + "type": "number", + "format": "double" + }, + "category": { + "$ref": "#/components/schemas/AppCategoryName" + }, + "description": { "type": "string" }, + "revision": { + "type": "number", + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/ProjectStatusName" + }, "user_name": { "type": "string" }, - "devices": { + "interpreter": { + "type": "string" + }, + "version": { + "$ref": "#/components/schemas/Version" + }, + "badges": { "items": { "type": "string" }, "type": "array" + }, + "dependencies": { + "items": { + "$ref": "#/components/schemas/Dependency" + }, + "type": "array" + }, + "states": { + "items": { + "$ref": "#/components/schemas/ProjectStatusOnBadge" + }, + "type": "array" + }, + "versions": { + "items": { + "$ref": "#/components/schemas/Version" + }, + "type": "array" + }, + "votes": { + "items": { + "$ref": "#/components/schemas/VoteFromUser" + }, + "type": "array" + }, + "warnings": { + "items": { + "$ref": "#/components/schemas/WarningFromUser" + }, + "type": "array" + }, + "collaborators": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array" } }, - "required": [ - "name", - "slug", - "description", - "category_slug", - "user_name", - "devices" - ], + "required": ["slug", "user_id", "created_at", "updated_at", "category"], + "type": "object", + "additionalProperties": false + }, + "Uint8Array": { + "description": "A typed array of 8-bit unsigned integer values. The contents are initialized to 0. If the\nrequested number of bytes could not be allocated an exception is raised.", + "properties": {}, "type": "object", "additionalProperties": false } @@ -110,7 +598,7 @@ "application/json": { "schema": { "items": { - "$ref": "#/components/schemas/Device" + "$ref": "#/components/schemas/Badge" }, "type": "array" } @@ -158,7 +646,7 @@ "application/json": { "schema": { "items": { - "$ref": "#/components/schemas/App" + "$ref": "#/components/schemas/Project" }, "type": "array" } @@ -166,7 +654,7 @@ } } }, - "description": "Get list of apps, optionally limited by page start/length and/or filtered by category", + "description": "Get list of apps, optionally limited by page start/length and/or filtered by categorySlug", "tags": ["public"], "security": [], "parameters": [ @@ -209,14 +697,14 @@ }, "/api/v3/apps/{slug}": { "get": { - "operationId": "GetAppDetails", + "operationId": "GetApp", "responses": { "200": { "description": "Ok", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AppDetails" + "$ref": "#/components/schemas/Project" } } } @@ -253,6 +741,160 @@ ] } }, + "/api/v3/apps/{slug}/files/latest/{filePath}": { + "get": { + "operationId": "GetLatestPublishedFile", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Uint8Array" + } + } + } + } + }, + "description": "get the latest published version of a file in the project", + "tags": ["public"], + "security": [], + "parameters": [ + { + "in": "path", + "name": "slug", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "filePath", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/v3/apps/{slug}/files/rev{revision}/{filePath}": { + "get": { + "operationId": "GetFileForVersion", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Uint8Array" + } + } + } + } + }, + "description": "get a file for a specific version of the project", + "tags": ["public"], + "security": [], + "parameters": [ + { + "in": "path", + "name": "slug", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "revision", + "required": true, + "schema": { + "format": "double", + "type": "number" + } + }, + { + "in": "path", + "name": "filePath", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/v3/apps/{slug}/zip/latest": { + "get": { + "operationId": "GetLatestPublishedZip", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Uint8Array" + } + } + } + } + }, + "description": "get the latest published version of the app in zip format", + "tags": ["public"], + "security": [], + "parameters": [ + { + "in": "path", + "name": "slug", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "/api/v3/apps/{slug}/zip/rev{revision}": { + "get": { + "operationId": "GetZipForVersion", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Uint8Array" + } + } + } + } + }, + "description": "get the app zip for a specific version of the project", + "tags": ["public"], + "security": [], + "parameters": [ + { + "in": "path", + "name": "slug", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "revision", + "required": true, + "schema": { + "format": "double", + "type": "number" + } + } + ] + } + }, "/api/v3/apps/{slug}/version": { "post": { "operationId": "CreateVersion", @@ -263,7 +905,7 @@ "application/json": { "schema": { "items": { - "$ref": "#/components/schemas/Device" + "$ref": "#/components/schemas/Badge" }, "type": "array" } diff --git a/src/auth/jwt.ts b/src/auth/jwt.ts index 1c871c5..18b7f2a 100644 --- a/src/auth/jwt.ts +++ b/src/auth/jwt.ts @@ -1,9 +1,4 @@ -import { - ErrorType, - BadgeHubApiError, - NotAuthorizedError, - NotAuthenticatedError, -} from "../error"; +import { ErrorType, NotAuthenticatedError, NotAuthorizedError } from "../error"; import { isAdmin, isContributor, UserRole } from "./role"; import { JWTPayload, jwtVerify } from "jose"; import { NextFunction, Request, Response } from "express"; diff --git a/src/controllers/private-rest.ts b/src/controllers/private-rest.ts index 774781e..752eb08 100644 --- a/src/controllers/private-rest.ts +++ b/src/controllers/private-rest.ts @@ -1,5 +1,5 @@ import { Path, Post, Route, Tags } from "tsoa"; -import { Device } from "../db/models"; +import { Badge } from "@domain/readModels/Badge"; @Route("/api/v3") @Tags("public") @@ -8,7 +8,7 @@ export class PrivateRestController { * Get list of devices (badges) */ @Post("/apps/{slug}/version") - public async createVersion(@Path() slug: string): Promise { + public async createVersion(@Path() slug: string): Promise { throw new Error("Not implemented"); } } diff --git a/src/controllers/public-rest.test.ts b/src/controllers/public-rest.test.ts index 208bb5b..3be10e4 100644 --- a/src/controllers/public-rest.test.ts +++ b/src/controllers/public-rest.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import request from "supertest"; import app from "../app"; import { RegisterRoutes } from "@generated/routes"; diff --git a/src/controllers/public-rest.ts b/src/controllers/public-rest.ts index 5cfd218..171f2c2 100644 --- a/src/controllers/public-rest.ts +++ b/src/controllers/public-rest.ts @@ -1,8 +1,11 @@ -import pg, { Pool } from "pg"; -import { Get, Path, Query, Res, Route, Tags } from "tsoa"; import type { TsoaResponse } from "tsoa"; -import { getPool } from "../db/connectionPool"; -import { App, AppDetails, Category, Device } from "../db/models"; +import { Get, Path, Query, Res, Route, Tags } from "tsoa"; +import type { BadgeHubDataPort } from "@domain/BadgeHubDataPort"; +import { Project } from "@domain/readModels/app/Project"; +import { BadgeHubDataPostgresAdapter } from "@db/BadgeHubDataPostgresAdapter"; + +import { Badge } from "@domain/readModels/Badge"; +import { Category } from "@domain/readModels/app/Category"; /** * The code is annotated so that OpenAPI documentation can be generated with tsoa @@ -19,20 +22,16 @@ import { App, AppDetails, Category, Device } from "../db/models"; @Route("/api/v3") @Tags("public") export class PublicRestController { - private pool: Pool; - public constructor() { - this.pool = getPool(); - } + public constructor( + private badgeHubData: BadgeHubDataPort = new BadgeHubDataPostgresAdapter() + ) {} /** * Get list of devices (badges) */ @Get("/devices") - public async getDevices(): Promise { - const result = await this.pool.query( - `select name, slug from badges` - ); - return result.rows; + public async getDevices(): Promise { + return await this.badgeHubData.getBadges(); } /** @@ -40,97 +39,85 @@ export class PublicRestController { */ @Get("/categories") public async getCategories(): Promise { - const result = await this.pool.query( - `select name, slug from categories` - ); - return result.rows; + return await this.badgeHubData.getCategories(); } /** - * Get list of apps, optionally limited by page start/length and/or filtered by category + * Get list of apps, optionally limited by page start/length and/or filtered by categorySlug */ @Get("/apps") public async getApps( @Query() pageStart?: number, @Query() pageLength?: number, - @Query() category?: string, + @Query() category?: Category["slug"], @Query() device?: string - ): Promise { - const mainQuery = ` - select p.name, p.slug, c.slug as category_slug, u.name as user_name - from projects p - inner join categories c on p.category_id = c.id - inner join users u on p.user_id = u.id`; - - const badgeQuery = ` - inner join badge_project bp on bp.project_id = p.id - inner join badges b on b.id = bp.badge_id`; - - let result: pg.QueryResult; - if (category && !device) { - result = await this.pool.query( - `${mainQuery} - where c.slug = $3 - limit $1 offset $2 - `, - [pageLength ?? null, pageStart ?? 0, category] - ); - } else if (!category && device) { - result = await this.pool.query( - `${mainQuery} ${badgeQuery} - where b.slug=$3 - limit $1 offset $2 - `, - [pageLength ?? null, pageStart ?? 0, device] - ); - } else if (category && device) { - result = await this.pool.query( - `${mainQuery} ${badgeQuery} - where c.slug = $3 and b.slug=$4 - limit $1 offset $2 - `, - [pageLength ?? null, pageStart ?? 0, category, device] - ); - } else { - result = await this.pool.query( - `${mainQuery} - limit $1 offset $2 - `, - [pageLength ?? null, pageStart ?? 0] - ); - } - return result.rows; + ): Promise { + return await this.badgeHubData.getProjects({ + pageStart, + pageLength, + badgeSlug: device, + categorySlug: category, + }); } /** * Get app details */ @Get("/apps/{slug}") - public async getAppDetails( + public async getApp( @Path() slug: string, @Res() notFoundResponse: TsoaResponse<404, { reason: string }> - ): Promise { - const result = await this.pool.query( - `select p.id, p.name, p.slug, p.description, c.slug as category_slug, u.name as user_name - from projects p - inner join categories c on p.category_id = c.id - inner join users u on p.user_id = u.id - where p.slug = $1`, - [slug] - ); - if (result.rows[0]) { - const projectId = result.rows[0].id; - const badgeResult = await this.pool.query( - `select b.slug from badge_project bp inner join badges b on bp.badge_id=b.id where project_id=$1`, - [projectId] - ); - const devices = badgeResult.rows.map((badge: Device) => badge.slug); - const { id, ...resultWithoutId } = result.rows[0]; - return { ...resultWithoutId, devices }; - } else { + ): Promise { + const details = await this.badgeHubData.getProject(slug); + if (!details) { return notFoundResponse(404, { reason: `No app with slug '${slug}' found`, }); } + return details; + } + + /** + * get the latest published version of a file in the project + */ + @Get("/apps/{slug}/files/latest/{filePath}") + public async getLatestPublishedFile( + @Path() slug: string, + @Path() filePath: string + ): Promise { + return await this.badgeHubData.getFileContents(slug, "latest", filePath); + } + + /** + * get a file for a specific version of the project + */ + @Get(`/apps/{slug}/files/rev{revision}/{filePath}`) + public async getFileForVersion( + @Path() slug: string, + @Path() revision: number, + @Path() filePath: string + ): Promise { + return await this.badgeHubData.getFileContents(slug, revision, filePath); + } + + /** + * get the latest published version of the app in zip format + */ + @Get("/apps/{slug}/zip/latest") + public async getLatestPublishedZip( + @Path() slug: string + ): Promise { + return await this.badgeHubData.getVersionZipContents(slug, "latest"); + } + + /** + * get the app zip for a specific version of the project + */ + @Get(`/apps/{slug}/zip/rev{revision}`) + public async getZipForVersion( + @Path() slug: string, + @Path() revision: number + ): Promise { + return await this.badgeHubData.getVersionZipContents(slug, revision); } } diff --git a/src/db/BadgeHubDataPostgresAdapter.ts b/src/db/BadgeHubDataPostgresAdapter.ts new file mode 100644 index 0000000..21bf21b --- /dev/null +++ b/src/db/BadgeHubDataPostgresAdapter.ts @@ -0,0 +1,169 @@ +import { BadgeHubDataPort } from "@domain/BadgeHubDataPort"; +import { Badge } from "@domain/readModels/Badge"; +import { Project } from "@domain/readModels/app/Project"; +import { User } from "@domain/readModels/app/User"; +import { Version } from "@domain/readModels/app/Version"; +import { Category } from "@domain/readModels/app/Category"; +import { Pool } from "pg"; +import { getPool } from "@db/connectionPool"; +import sql, { join } from "sql-template-tag"; +import { DBBadge } from "@db/models/DBBadge"; +import { + getBaseSelectProjectQuery, + ProjectQueryResponse, + projectQueryResponseToReadModel, +} from "@db/sqlHelpers/projectQuery"; +import { + convertDatedData, + extractDatedDataConverted, + OmitDatedData, + stripDatedData, + timestampTZToDate, +} from "@db/sqlHelpers/dbDates"; +import { DBVersion } from "@db/models/app/DBVersion"; +import { DBAppMetadataJSON } from "@db/models/app/DBAppMetadataJSON"; +import { DBCategory } from "@db/models/app/DBCategory"; + +export class BadgeHubDataPostgresAdapter implements BadgeHubDataPort { + private readonly pool: Pool; + + constructor() { + this.pool = getPool(); + } + + async getCategories(): Promise { + const dbCategoryNames: OmitDatedData[] = await this.pool + .query(sql`select name, slug from categories`) + .then((res) => res.rows); + return dbCategoryNames.map((dbCategory) => dbCategory); + } + + async getProject(projectSlug: string): Promise { + return (await this.getProjects({ projectSlug }))[0]!; + } + + // TODO test + async getDraftVersion(projectSlug: string): Promise { + const selectVersionIdForProject = sql`select version_id from projects p where p.slug = ${projectSlug}`; + const dbVersion: DBVersion & { app_metadata: DBAppMetadataJSON } = + await this.pool + .query( + sql`select + v.id, + v.revision, + v.semantic_version, + v.zip, + v.size_of_zip, + v.git_commit_id, + v.published_at, + v.download_count, + v.project_slug, + v.app_metadata_json_id, + v.created_at, + v.updated_at, + v.deleted_at, + to_jsonb(m) as app_metadata + from versions v + left join app_metadata_jsons m on v.app_metadata_json_id = m.id + where v.id = (${selectVersionIdForProject}) + ` + ) + .then((res) => res.rows[0]); + const { id, ...appMetadataWithoutId } = dbVersion.app_metadata; + return { + ...convertDatedData(dbVersion), + files: [], // TODO + app_metadata: stripDatedData(appMetadataWithoutId), // TODO + published_at: timestampTZToDate(dbVersion.published_at), + }; + } + + getUser(userId: string): Promise { + throw new Error("Method not implemented."); + } + + getFileContents( + projectSlug: string, + versionRevision: number | "draft" | "latest", + filePath: string + ): Promise { + throw new Error("Method not implemented."); + } + + getVersionZipContents( + projectSlug: string, + versionRevision: number + ): Promise { + throw new Error("Method not implemented."); + } + + async getBadges(): Promise { + const dbBadges: DBBadge[] = await this.pool + .query( + sql`select slug, name + from badges` + ) + .then((res) => res.rows); + return dbBadges.map((dbBadge) => ({ + slug: dbBadge.slug, + name: dbBadge.name, + ...extractDatedDataConverted(dbBadge), + })); + } + + async getProjects(filter?: { + projectSlug?: Project["slug"]; + pageStart?: number; + pageLength?: number; + badgeSlug?: Badge["slug"]; + categorySlug?: Category["slug"]; + }): Promise { + let query = getBaseSelectProjectQuery(); + if (filter?.badgeSlug) { + query = sql`${query} + inner join project_statuses_on_badges psb on p.slug = psb.project_slug and psb.badge_slug = ${filter.badgeSlug}`; + } + query = sql`${query} where p.deleted_at is null`; + + if (filter?.categorySlug) { + query = sql`${query} and c.slug = ${filter.categorySlug}`; + } + + if (filter?.projectSlug) { + query = sql`${query} and p.slug = ${filter.projectSlug}`; + } + + if (filter?.pageLength) { + query = sql`${query} + limit + ${filter.pageLength} + offset + ${filter?.pageStart ?? 0}`; + } + + const projects: ProjectQueryResponse[] = await this.pool + .query(query) + .then((res) => res.rows); + const badgesMap = await this._getBadgesMap(projects.map((p) => p.slug)); + return projects.map(projectQueryResponseToReadModel).map((p) => ({ + ...p, + badges: badgesMap[p.slug], + })); + } + + private async _getBadgesMap(projectSlugs: Project["slug"][]) { + if (!projectSlugs.length) { + return {}; + } + const query = sql`select project_slug, json_agg(badge_slug) as badges from project_statuses_on_badges where project_slug in (${join(projectSlugs)}) group by project_slug`; + + const badges: { project_slug: Project["slug"]; badges: Badge["slug"][] }[] = + await this.pool.query(query).then((res) => res.rows); + + const badgesMap: Record = + Object.fromEntries( + badges.map(({ project_slug, badges }) => [project_slug, badges]) + ); + return badgesMap; + } +} diff --git a/src/db/DBTypes.ts b/src/db/DBTypes.ts new file mode 100644 index 0000000..6208a86 --- /dev/null +++ b/src/db/DBTypes.ts @@ -0,0 +1 @@ +export type TimestampTZ = string; // it would be cool if we could use the following: `${string}-${string}-${string} ${string}:${string}:${string}.${string} +${string}`, but this would result in a TSOA generation error diff --git a/src/db/models/DBBadge.ts b/src/db/models/DBBadge.ts new file mode 100644 index 0000000..64fc4e2 --- /dev/null +++ b/src/db/models/DBBadge.ts @@ -0,0 +1,12 @@ +import { DBDatedData } from "./app/DBDatedData"; + +export interface BadgeSlugRelation { + badge_slug: DBBadge["slug"]; +} +export interface DBInsertBadge { + slug: string; + name: string; +} + +// table name: badges +export interface DBBadge extends DBInsertBadge, DBDatedData {} diff --git a/src/db/models/DBProjectStatusOnBadge.ts b/src/db/models/DBProjectStatusOnBadge.ts new file mode 100644 index 0000000..7b47772 --- /dev/null +++ b/src/db/models/DBProjectStatusOnBadge.ts @@ -0,0 +1,13 @@ +import { DBDatedData } from "./app/DBDatedData"; +import { BadgeSlugRelation } from "./DBBadge"; +import { ProjectStatusName } from "@domain/readModels/app/Project"; +import { ProjectSlugRelation } from "@db/models/app/DBProject"; + +// table name: project_statuses_on_badges +export interface DBProjectStatusOnBadge + extends DBDatedData, + BadgeSlugRelation, + ProjectSlugRelation { + id: number; + status: ProjectStatusName; // Status for this project for this particular badge +} diff --git a/src/db/models/README.md b/src/db/models/README.md new file mode 100644 index 0000000..451756b --- /dev/null +++ b/src/db/models/README.md @@ -0,0 +1,7 @@ +## Philosophy of the db models + +In the db models, we try to stick to 2 principles + +- DRY, no data duplication, unless it's accidental duplication +- No arrays. Arrays make querying and switching to another DB more difficult. So instead of arrays, we try to work with relation tables. + - There is currently an exception here for the MetaDataFileContents, there the mappings are records and arrays and records with arrays, this is because the data comes from the json file and we could also just store it as stringified JSON. diff --git a/src/db/models/app/DBAppMetadataJSON.ts b/src/db/models/app/DBAppMetadataJSON.ts new file mode 100644 index 0000000..39b6b65 --- /dev/null +++ b/src/db/models/app/DBAppMetadataJSON.ts @@ -0,0 +1,19 @@ +// This represents the contents of the metadata.json file that we have in the app. +// This is only put into the database for making interesting read queries possible. +// These contents should never be updated directly, but instead the metadata.json file should be modified and then read out again in order to fill the fields here. +// Metadata for a published version cannot be edited, except by republishing this version which would overwrite the old version. + +import { AppMetadataJSON } from "@domain/readModels/app/AppMetadataJSON"; +import { DBDatedData } from "@db/models/app/DBDatedData"; + +export interface AppMetadataJSONRelation { + app_metadata_json_id: DBAppMetadataJSON["id"]; +} + +// table name: app_metadata_json +export interface DBInsertAppMetadataJSON extends AppMetadataJSON {} +export interface DBAppMetadataJSON + extends DBDatedData, + DBInsertAppMetadataJSON { + id: number; +} diff --git a/src/db/models/app/DBCategory.ts b/src/db/models/app/DBCategory.ts new file mode 100644 index 0000000..342440e --- /dev/null +++ b/src/db/models/app/DBCategory.ts @@ -0,0 +1,8 @@ +import { DBDatedData } from "@db/models/app/DBDatedData"; +import { AppCategoryName } from "@domain/readModels/app/Category"; + +// table name: categories +export interface DBCategory extends DBDatedData { + name: AppCategoryName; + slug: string; +} diff --git a/src/db/models/app/DBDatedData.ts b/src/db/models/app/DBDatedData.ts new file mode 100644 index 0000000..2c5d110 --- /dev/null +++ b/src/db/models/app/DBDatedData.ts @@ -0,0 +1,7 @@ +import { TimestampTZ } from "@db/DBTypes"; + +export interface DBDatedData { + created_at: TimestampTZ; // Creation date + updated_at: TimestampTZ; // Last update date + deleted_at?: TimestampTZ; // Optional Deletion date +} diff --git a/src/db/models/app/DBFileMetadata.ts b/src/db/models/app/DBFileMetadata.ts new file mode 100644 index 0000000..0dedc3e --- /dev/null +++ b/src/db/models/app/DBFileMetadata.ts @@ -0,0 +1,19 @@ +import { UserRelation } from "./DBUser"; +import { DBDatedData } from "./DBDatedData"; + +export interface FileMetadataRelation { + file_metadata_id: DBFileMetadata["id"]; +} + +// table name: file_metadata +export interface DBFileMetadata extends UserRelation, DBDatedData { + id: number; + lintable?: boolean; // Some files in a project can be auto-lintable, others not + extension: string; + baseName: string; + editable?: boolean; + file_content_language?: string; // Eg. python, c++ + mime?: string; + size_of_content: number; + crc32: string; +} diff --git a/src/db/models/app/DBProject.ts b/src/db/models/app/DBProject.ts new file mode 100644 index 0000000..e79d7c7 --- /dev/null +++ b/src/db/models/app/DBProject.ts @@ -0,0 +1,19 @@ +import { UserRelation } from "./DBUser"; +import { DBDatedData } from "./DBDatedData"; +import { VersionRelation } from "./DBVersion"; +import { ProjectSlug } from "@domain/readModels/app/Project"; + +export interface DBInsertProject + extends Partial, // The Latest DBVersion + UserRelation { + slug: ProjectSlug; // The directory name of this app + git?: string; // repository url + allow_team_fixes?: boolean; +} + +// table name: projects +export interface DBProject extends DBInsertProject, DBDatedData {} + +export interface ProjectSlugRelation { + project_slug: DBProject["slug"]; +} diff --git a/src/db/models/app/DBUser.ts b/src/db/models/app/DBUser.ts new file mode 100644 index 0000000..53c7177 --- /dev/null +++ b/src/db/models/app/DBUser.ts @@ -0,0 +1,20 @@ +import { DBDatedData } from "./DBDatedData"; +import { TimestampTZ } from "@db/DBTypes"; + +export interface UserRelation { + user_id: DBUser["id"]; +} +export interface DBInsertUser { + id: string; + email: string; + admin?: boolean; + name: string; + password: string; + remember_token?: string; + editor?: string; + public?: boolean; + show_projects?: boolean; + email_verified_at?: TimestampTZ; +} +// table name: users +export interface DBUser extends DBInsertUser, DBDatedData {} diff --git a/src/db/models/app/DBVersion.ts b/src/db/models/app/DBVersion.ts new file mode 100644 index 0000000..04b116b --- /dev/null +++ b/src/db/models/app/DBVersion.ts @@ -0,0 +1,24 @@ +import { AppMetadataJSONRelation } from "./DBAppMetadataJSON"; +import { DBDatedData } from "./DBDatedData"; +import { TimestampTZ } from "@db/DBTypes"; +import { ProjectSlugRelation } from "@db/models/app/DBProject"; + +export interface VersionRelation { + version_id: DBVersion["id"]; +} +export interface DBInsertVersion + extends AppMetadataJSONRelation, + ProjectSlugRelation { + id: number; + revision: number; + semantic_version?: string; // Changed! Semantic version + zip?: string; + size_of_zip?: number; + git_commit_id?: string; + published_at?: TimestampTZ; + download_count?: number; +} + +export interface DBVersion extends DBInsertVersion, DBDatedData { + download_count: number; +} diff --git a/src/db/models/app/DBVersionedDependency.ts b/src/db/models/app/DBVersionedDependency.ts new file mode 100644 index 0000000..c6b185d --- /dev/null +++ b/src/db/models/app/DBVersionedDependency.ts @@ -0,0 +1,15 @@ +import { ProjectSlugRelation } from "./DBProject"; + +type VersionedDependencyId = number; + +export interface VersionedDependencyRelation { + versioned_dependency_id: DBVersionedDependency["id"]; +} + +// table versioned_dependencies +export interface DBVersionedDependency extends ProjectSlugRelation { + semantic_version_range: string; + project_slug: string; + depends_on_project_slug: string; + id: VersionedDependencyId; +} diff --git a/src/db/models/app/DBVote.ts b/src/db/models/app/DBVote.ts new file mode 100644 index 0000000..63126b1 --- /dev/null +++ b/src/db/models/app/DBVote.ts @@ -0,0 +1,9 @@ +import { Vote } from "@domain/readModels/app/VoteFromUser"; + +export interface VoteRelation { + vote_id: DBVote["id"]; +} + +export interface DBVote extends Vote { + id: number; +} diff --git a/src/db/models/app/DBVoteFromUser.ts b/src/db/models/app/DBVoteFromUser.ts new file mode 100644 index 0000000..d6b3f1d --- /dev/null +++ b/src/db/models/app/DBVoteFromUser.ts @@ -0,0 +1,14 @@ +import { VoteRelation } from "./DBVote"; +import { UserRelation } from "./DBUser"; +import { DBDatedData } from "./DBDatedData"; + +export interface VoteFromUserRelation { + vote_from_user_id: DBVoteFromUser["id"]; +} + +export interface DBVoteFromUser + extends VoteRelation, + UserRelation, + DBDatedData { + id: number; +} diff --git a/src/db/models/app/DBWarning.ts b/src/db/models/app/DBWarning.ts new file mode 100644 index 0000000..6039fac --- /dev/null +++ b/src/db/models/app/DBWarning.ts @@ -0,0 +1,9 @@ +import { Warning } from "@domain/readModels/app/WarningFromUser"; + +export interface WarningRelation { + warning_id: DBWarning["id"]; +} + +export interface DBWarning extends Warning { + id: number; +} diff --git a/src/db/models/app/DBWarningFromUser.ts b/src/db/models/app/DBWarningFromUser.ts new file mode 100644 index 0000000..d7a2621 --- /dev/null +++ b/src/db/models/app/DBWarningFromUser.ts @@ -0,0 +1,14 @@ +import { WarningRelation } from "./DBWarning"; +import { UserRelation } from "./DBUser"; +import { DBDatedData } from "./DBDatedData"; + +export interface WarningFromUserRelation { + warning_from_user_id: DBWarningFromUser["id"]; +} + +export interface DBWarningFromUser + extends WarningRelation, + UserRelation, + DBDatedData { + id: number; +} diff --git a/src/db/models/app/relations/ProjectHasCollaborators.ts b/src/db/models/app/relations/ProjectHasCollaborators.ts new file mode 100644 index 0000000..02fcae9 --- /dev/null +++ b/src/db/models/app/relations/ProjectHasCollaborators.ts @@ -0,0 +1,9 @@ +import { UserRelation } from "../DBUser"; +import { ProjectSlugRelation } from "../DBProject"; + +// TODO implement table +export interface ProjectHasCollaborators + extends ProjectSlugRelation, + UserRelation { + id: number; +} diff --git a/src/db/models/app/relations/ProjectHasVotes.ts b/src/db/models/app/relations/ProjectHasVotes.ts new file mode 100644 index 0000000..769fa69 --- /dev/null +++ b/src/db/models/app/relations/ProjectHasVotes.ts @@ -0,0 +1,9 @@ +import { ProjectSlugRelation } from "../DBProject"; +import { VoteFromUserRelation } from "../DBVoteFromUser"; + +// TODO implement table +export interface ProjectHasVotes + extends VoteFromUserRelation, + ProjectSlugRelation { + id: number; +} diff --git a/src/db/models/app/relations/ProjectHasWarnings.ts b/src/db/models/app/relations/ProjectHasWarnings.ts new file mode 100644 index 0000000..cc0264a --- /dev/null +++ b/src/db/models/app/relations/ProjectHasWarnings.ts @@ -0,0 +1,11 @@ +import { ProjectSlugRelation } from "../DBProject"; +import { WarningRelation } from "../DBWarning"; +import { WarningFromUserRelation } from "../DBWarningFromUser"; + +// TODO implement table +export interface ProjectHasWarnings + extends WarningFromUserRelation, + ProjectSlugRelation, + WarningRelation { + id: number; +} diff --git a/src/db/models/app/relations/VersionHasFiles.ts b/src/db/models/app/relations/VersionHasFiles.ts new file mode 100644 index 0000000..bd9b851 --- /dev/null +++ b/src/db/models/app/relations/VersionHasFiles.ts @@ -0,0 +1,7 @@ +import { VersionRelation } from "../DBVersion"; +import { FileMetadataRelation } from "@db/models/app/DBFileMetadata"; + +// TODO implement table +export interface VersionHasFiles extends VersionRelation, FileMetadataRelation { + id: number; +} diff --git a/src/db/models/index.ts b/src/db/models/index.ts deleted file mode 100644 index 0b0f6a5..0000000 --- a/src/db/models/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface Device { - name: string; - slug: string; -} - -export interface Category { - name: string; - slug: string; -} - -export interface App { - name: string; - slug: string; - category_slug: string; - user_name: string; -} - -export interface AppDetails { - name: string; - slug: string; - description: string; - category_slug: string; - user_name: string; - devices: string[]; -} diff --git a/src/db/sqlHelpers/dbDates.ts b/src/db/sqlHelpers/dbDates.ts new file mode 100644 index 0000000..63c692e --- /dev/null +++ b/src/db/sqlHelpers/dbDates.ts @@ -0,0 +1,44 @@ +import { DBDatedData } from "@db/models/app/DBDatedData"; +import { DatedData } from "@domain/readModels/app/DatedData"; +import moment from "moment/moment"; + +export function extractDatedDataConverted(dbDatedData: DBDatedData): DatedData { + const datedData: DatedData = { + created_at: timestampTZToDate(dbDatedData.created_at), + updated_at: timestampTZToDate(dbDatedData.updated_at), + }; + if (dbDatedData.deleted_at) { + datedData.deleted_at = timestampTZToDate(dbDatedData.deleted_at); + } + return datedData; +} + +export function timestampTZToDate( + dbDate: T +): T extends undefined ? undefined : Date { + return ( + dbDate !== undefined ? moment(dbDate).toDate() : undefined + ) as T extends undefined ? undefined : Date; +} + +export type OmitDatedData = Omit< + T, + "deleted_at" | "updated_at" | "created_at" +>; + +export function convertDatedData( + datedData: T +): OmitDatedData & DatedData { + return { + ...stripDatedData(datedData), + ...extractDatedDataConverted(datedData), + }; +} + +export function stripDatedData( + datedData: T +): OmitDatedData { + const { deleted_at, updated_at, created_at, ...dataWithoutDatedData } = + datedData; + return dataWithoutDatedData; +} diff --git a/src/db/sqlHelpers/projectQuery.ts b/src/db/sqlHelpers/projectQuery.ts new file mode 100644 index 0000000..bc6e935 --- /dev/null +++ b/src/db/sqlHelpers/projectQuery.ts @@ -0,0 +1,74 @@ +import { Project } from "@domain/readModels/app/Project"; +import moment from "moment/moment"; +import { DBProject } from "@db/models/app/DBProject"; +import { DBVersion } from "@db/models/app/DBVersion"; +import { DBAppMetadataJSON as DBMetadataFileContents } from "@db/models/app/DBAppMetadataJSON"; +import { DBUser } from "@db/models/app/DBUser"; +import sql from "sql-template-tag"; +import { extractDatedDataConverted } from "@db/sqlHelpers/dbDates"; +import { Category } from "@domain/readModels/app/Category"; + +export function getBaseSelectProjectQuery() { + return sql`select p.slug, + p.git, + p.allow_team_fixes, + p.user_id, + p.created_at, + p.updated_at, + p.deleted_at, + v.semantic_version, + v.git_commit_id, + v.published_at, + v.revision, + v.size_of_zip, + m.category, + m.description, + m.interpreter, + m.license_file, + m.name, + u.name as author_name + from projects p + left join users u on p.user_id = u.id and u.deleted_at is null + left join versions v on p.version_id = v.id + left join app_metadata_jsons m on v.app_metadata_json_id = m.id and v.deleted_at is null + left join categories c on m.category = c.name and c.deleted_at is null + `; +} + +export const projectQueryResponseToReadModel = ( + enrichedDBProject: ProjectQueryResponse +): Project => { + return { + version: undefined, // TODO + allow_team_fixes: false, + user_id: enrichedDBProject.user_id, + user_name: enrichedDBProject.author_name, // todo maybe change to email, full id or object with multiple fields + category: enrichedDBProject.category || "Uncategorised", + collaborators: [], // TODO + description: enrichedDBProject.description, + download_counter: undefined, // TODO + git: enrichedDBProject.git, + git_commit_id: enrichedDBProject.git_commit_id, + interpreter: enrichedDBProject.interpreter, + license: enrichedDBProject.license_file, // TODO check what we should do with the license, possibly we could say that this is either a path or 'MIT'|..., but then still we should read out the licens somewhere if it is a file. + name: enrichedDBProject.name, + published_at: moment(enrichedDBProject.published_at).toDate(), + revision: enrichedDBProject.revision, + size_of_content: undefined, // TODO + size_of_zip: enrichedDBProject.size_of_zip, + slug: enrichedDBProject.slug, + states: undefined, + status: undefined, // TODO + versions: undefined, // TODO + dependencies: undefined, // TODO + votes: undefined, // TODO + warnings: undefined, // TODO + ...extractDatedDataConverted(enrichedDBProject), + }; +}; +export type ProjectQueryResponse = DBProject & + DBVersion & + DBMetadataFileContents & { + author_name: DBUser["name"]; + category_slug: Category["slug"]; + }; diff --git a/src/domain/BadgeHubDataPort.ts b/src/domain/BadgeHubDataPort.ts new file mode 100644 index 0000000..5884109 --- /dev/null +++ b/src/domain/BadgeHubDataPort.ts @@ -0,0 +1,36 @@ +import { Project, ProjectSlug } from "@domain/readModels/app/Project"; +import { Version } from "@domain/readModels/app/Version"; +import { User } from "@domain/readModels/app/User"; +import { FileMetadata } from "@domain/readModels/app/FileMetadata"; +import { Badge } from "@domain/readModels/Badge"; +import { Category } from "@domain/readModels/app/Category"; + +export interface BadgeHubDataPort { + getProject(projectSlug: ProjectSlug): Promise; + + getDraftVersion(projectSlug: ProjectSlug): Promise; + + getUser(userId: User["id"]): Promise; + + getFileContents( + projectSlug: Project["slug"], + versionRevision: number | "draft" | "latest", + filePath: FileMetadata["name"] + ): Promise; + + getVersionZipContents( + projectSlug: Project["slug"], + versionRevision: number | "draft" | "latest" + ): Promise; + + getBadges(): Promise; + + getCategories(): Promise; + + getProjects(filter?: { + pageStart?: number; + pageLength?: number; + badgeSlug?: Badge["slug"]; + categorySlug?: Category["slug"]; + }): Promise; +} diff --git a/src/domain/readModels/Badge.ts b/src/domain/readModels/Badge.ts new file mode 100644 index 0000000..82ea161 --- /dev/null +++ b/src/domain/readModels/Badge.ts @@ -0,0 +1,9 @@ +import { DatedData } from "@domain/readModels/app/DatedData"; + +export interface BadgeRelation { + badge: Badge; +} +export interface Badge extends DatedData { + name: string; + slug: string; +} diff --git a/src/domain/readModels/ProjectStatusOnBadge.ts b/src/domain/readModels/ProjectStatusOnBadge.ts new file mode 100644 index 0000000..2ec4ece --- /dev/null +++ b/src/domain/readModels/ProjectStatusOnBadge.ts @@ -0,0 +1,7 @@ +import { DatedData } from "@domain/readModels/app/DatedData"; +import { BadgeRelation } from "./Badge"; +import { ProjectStatusName } from "@domain/readModels/app/Project"; + +export interface ProjectStatusOnBadge extends BadgeRelation, DatedData { + status: ProjectStatusName; // Status for this project for this particular badge +} diff --git a/src/domain/readModels/app/AppMetadataJSON.ts b/src/domain/readModels/app/AppMetadataJSON.ts new file mode 100644 index 0000000..bd52160 --- /dev/null +++ b/src/domain/readModels/app/AppMetadataJSON.ts @@ -0,0 +1,26 @@ +// This represents the contents of the metadata.json file that we have in the app. +// This is only put into the database for making interesting read queries possible. +// These contents should never be updated directly, but instead the metadata.json file should be modified and then read out again in order to fill the fields here. +// Metadata for a published version cannot be edited, except by republishing this version which would overwrite the old version. +import { Category } from "@domain/readModels/app/Category"; +import { Badge } from "@domain/readModels/Badge"; + +export interface AppMetadataJSON { + name?: string; + description?: string; + category?: Category["name"]; + author?: string; // The name of the user_name + icon?: string; // The relative icon path + license_file?: string; // Optional path of the license file for this project. If not set, then LICENSE.md will be used. + is_library?: boolean; // Whether this app can be used as a library by other apps + is_hidden?: boolean; // Whether this app should be shown in the launcher or not. Only useful for libraries. + semantic_version?: string; // Changed! [Semantic version](https://semver.org/) of the app, the semantic versioning is mostly relevant if the app is a library. Authors who don't use this semantic versioning will get a 0.x version with x just an number like we previously had the revision number. + interpreter?: string; // Changed! For example 'python' or the app slug of a 3rd party dependency of this app. + main_executable?: string; // Relative path of the executable file from this package that is the main executable file of this app. + main_executable_overrides?: Record; // Optional field to allow overriding the main_executable for a certain badge. + file_mappings?: Array<{ source: string; destination: string }>; // Changed! Mapping to tell the badge where some files in this app should be placed on the filesystem. Source is a relative path. Desitination can either be relative or absolute. + file_mappings_overrides?: Record< + Badge["slug"], + Array<{ source: string; destination: string }> + >; // Changed! optional field to allow overriding or adding a file mapping for a device name slug (key). +} diff --git a/src/domain/readModels/app/Category.ts b/src/domain/readModels/app/Category.ts new file mode 100644 index 0000000..0805862 --- /dev/null +++ b/src/domain/readModels/app/Category.ts @@ -0,0 +1,31 @@ +import { DBCategory } from "@db/models/app/DBCategory"; + +export type AppCategoryName = + | "Uncategorised" + | "Event related" + | "Games" + | "Graphics" + | "Hardware" + | "Utility" + | "Wearable" + | "Data" + | "Silly" + | "Hacking" + | "Troll" + | "Unusable" + | "Adult" + | "Virus" + | "Interpreter"; // Changed! the interpreter categorySlug was added here for the case of libraries. + +export interface CategorySlugRelation { + category_slug: DBCategory["slug"]; +} + +export interface CategoryNameRelation { + category_name: DBCategory["name"]; +} + +export interface Category { + name: AppCategoryName; + slug: string; +} diff --git a/src/domain/readModels/app/DatedData.ts b/src/domain/readModels/app/DatedData.ts new file mode 100644 index 0000000..41de343 --- /dev/null +++ b/src/domain/readModels/app/DatedData.ts @@ -0,0 +1,5 @@ +export interface DatedData { + created_at: Date; // Creation date + updated_at: Date; // Last update date + deleted_at?: Date; // Optional Deletion date +} diff --git a/src/domain/readModels/app/FileMetadata.ts b/src/domain/readModels/app/FileMetadata.ts new file mode 100644 index 0000000..4e4de15 --- /dev/null +++ b/src/domain/readModels/app/FileMetadata.ts @@ -0,0 +1,17 @@ +import { VersionRelation } from "./Version"; +import { UserRelation } from "./User"; +import { DatedData } from "./DatedData"; + +export interface FileMetadata extends VersionRelation, UserRelation, DatedData { + editable?: boolean; + lintable?: boolean; + extension: string; + baseName: string; + mime?: string; // Can include info about the programming language + size_of_content: number; + crc32: string; + + // Computed + size_formatted: string; + name: string; +} diff --git a/src/domain/readModels/app/Project.ts b/src/domain/readModels/app/Project.ts new file mode 100644 index 0000000..840c8eb --- /dev/null +++ b/src/domain/readModels/app/Project.ts @@ -0,0 +1,59 @@ +import { Version } from "./Version"; +import { User } from "./User"; +import { DatedData } from "./DatedData"; +import { Badge } from "../Badge"; +import { ProjectStatusOnBadge } from "../ProjectStatusOnBadge"; +import { AppMetadataJSON } from "./AppMetadataJSON"; +import { VoteFromUser } from "./VoteFromUser"; +import { WarningFromUser } from "./WarningFromUser"; +import { Category } from "@domain/readModels/app/Category"; + +export type ProjectStatusName = + | "working" + | "in_progress" + | "broken" + | "unknown"; + +export interface ProjectCore { + slug: string; + user_id: User["id"]; + git?: string; + allow_team_fixes?: boolean; +} + +export interface Project extends ProjectCore, DatedData { + // Computed + name?: string; + min_firmware?: number; // Smallest revision number that exists + max_firmware?: number; // Biggest revision number that exists + git_commit_id?: string; + published_at?: Date; // Last publish date + download_counter?: number; // Sum of all version download count + license?: string; // Eg. MIT + size_of_zip?: number; + size_of_content?: number; + category: Category["name"]; + description?: string; // description in metadata of latest version of the app + revision?: number; // latest revsion number of the app + status?: ProjectStatusName; // Status of newest version with a non-empty status + user_name?: string; // user->name + interpreter?: AppMetadataJSON["interpreter"]; // Interpreter for latest version of app + + // Relations + version?: Version; + badges?: Array; + dependencies?: Array; // Changed! We depend on a semantic version specification of a project instead of just the project. + states?: Array; + versions?: Array; + votes?: Array; + warnings?: Array; + collaborators?: Array; +} + +export type ProjectSlug = Project["slug"]; + +interface Dependency { + project_slug: ProjectSlug; + // Changed! semantic_version_range added + semantic_version_range: string; // Semantic version range specification that allows tilde, caret, wildcard specification of the version of a project that should be used. Following what is described here: https://python-poetry.org/docs/dependency-specification/ +} diff --git a/src/domain/readModels/app/User.ts b/src/domain/readModels/app/User.ts new file mode 100644 index 0000000..fb8b0f3 --- /dev/null +++ b/src/domain/readModels/app/User.ts @@ -0,0 +1,18 @@ +import { DatedData } from "./DatedData"; + +export interface UserRelation { + user: User; +} + +export interface User extends DatedData { + id: string; + email: string; + admin: boolean; + name: string; + password: string; + remember_token?: string; + editor: string; + public: boolean; + show_projects: boolean; + email_verified_at?: Date; +} diff --git a/src/domain/readModels/app/Version.ts b/src/domain/readModels/app/Version.ts new file mode 100644 index 0000000..7d2ecb8 --- /dev/null +++ b/src/domain/readModels/app/Version.ts @@ -0,0 +1,21 @@ +import { AppMetadataJSON } from "./AppMetadataJSON"; +import { DatedData } from "./DatedData"; +import { FileMetadata } from "./FileMetadata"; +import { Project } from "@domain/readModels/app/Project"; + +export interface VersionRelation { + version: Version; +} + +export interface Version extends DatedData { + revision: number; + semantic_version?: string; // Changed! Semantic version + zip?: string; + size_of_zip?: number; + git_commit_id?: string; + files: Array; + app_metadata: AppMetadataJSON; // Changed! New property that has the content of the metadata.json file that is installed on the app. + published_at?: Date; + download_count: number; + project_slug: Project["slug"]; +} diff --git a/src/domain/readModels/app/VoteFromUser.ts b/src/domain/readModels/app/VoteFromUser.ts new file mode 100644 index 0000000..955c973 --- /dev/null +++ b/src/domain/readModels/app/VoteFromUser.ts @@ -0,0 +1,9 @@ +import { UserRelation } from "./User"; +import { DatedData } from "./DatedData"; + +export interface Vote { + type: "up" | "down" | "pig"; + comment?: string; +} + +export interface VoteFromUser extends Vote, UserRelation, DatedData {} diff --git a/src/domain/readModels/app/WarningFromUser.ts b/src/domain/readModels/app/WarningFromUser.ts new file mode 100644 index 0000000..e9e5edf --- /dev/null +++ b/src/domain/readModels/app/WarningFromUser.ts @@ -0,0 +1,8 @@ +import { UserRelation } from "./User"; +import { DatedData } from "./DatedData"; + +export interface Warning { + description: string; +} + +export interface WarningFromUser extends Warning, UserRelation, DatedData {} diff --git a/src/generated/routes.ts b/src/generated/routes.ts index 643c57b..9097946 100644 --- a/src/generated/routes.ts +++ b/src/generated/routes.ts @@ -1,11 +1,8 @@ /* tslint:disable */ /* eslint-disable */ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa -import { - TsoaRoute, - fetchMiddlewares, - ExpressTemplateService, -} from "@tsoa/runtime"; +import type { TsoaRoute } from "@tsoa/runtime"; +import { fetchMiddlewares, ExpressTemplateService } from "@tsoa/runtime"; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa import { PublicRestController } from "./../controllers/public-rest.js"; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -20,52 +17,312 @@ import type { // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa const models: TsoaRoute.Models = { - Device: { + Badge: { dataType: "refObject", properties: { + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, name: { dataType: "string", required: true }, slug: { dataType: "string", required: true }, }, additionalProperties: false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + AppCategoryName: { + dataType: "refAlias", + type: { + dataType: "union", + subSchemas: [ + { dataType: "enum", enums: ["Uncategorised"] }, + { dataType: "enum", enums: ["Event related"] }, + { dataType: "enum", enums: ["Games"] }, + { dataType: "enum", enums: ["Graphics"] }, + { dataType: "enum", enums: ["Hardware"] }, + { dataType: "enum", enums: ["Utility"] }, + { dataType: "enum", enums: ["Wearable"] }, + { dataType: "enum", enums: ["Data"] }, + { dataType: "enum", enums: ["Silly"] }, + { dataType: "enum", enums: ["Hacking"] }, + { dataType: "enum", enums: ["Troll"] }, + { dataType: "enum", enums: ["Unusable"] }, + { dataType: "enum", enums: ["Adult"] }, + { dataType: "enum", enums: ["Virus"] }, + { dataType: "enum", enums: ["Interpreter"] }, + ], + validators: {}, + }, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa Category: { dataType: "refObject", properties: { - name: { dataType: "string", required: true }, + name: { ref: "AppCategoryName", required: true }, slug: { dataType: "string", required: true }, }, additionalProperties: false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - App: { + ProjectStatusName: { + dataType: "refAlias", + type: { + dataType: "union", + subSchemas: [ + { dataType: "enum", enums: ["working"] }, + { dataType: "enum", enums: ["in_progress"] }, + { dataType: "enum", enums: ["broken"] }, + { dataType: "enum", enums: ["unknown"] }, + ], + validators: {}, + }, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + Version: { + dataType: "refObject", + properties: { + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + revision: { dataType: "double", required: true }, + semantic_version: { dataType: "string" }, + zip: { dataType: "string" }, + size_of_zip: { dataType: "double" }, + git_commit_id: { dataType: "string" }, + files: { + dataType: "array", + array: { dataType: "refObject", ref: "FileMetadata" }, + required: true, + }, + app_metadata: { ref: "AppMetadataJSON", required: true }, + published_at: { dataType: "datetime" }, + download_count: { dataType: "double", required: true }, + project_slug: { dataType: "string", required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + User: { dataType: "refObject", properties: { + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + id: { dataType: "string", required: true }, + email: { dataType: "string", required: true }, + admin: { dataType: "boolean", required: true }, name: { dataType: "string", required: true }, - slug: { dataType: "string", required: true }, - category_slug: { dataType: "string", required: true }, - user_name: { dataType: "string", required: true }, + password: { dataType: "string", required: true }, + remember_token: { dataType: "string" }, + editor: { dataType: "string", required: true }, + public: { dataType: "boolean", required: true }, + show_projects: { dataType: "boolean", required: true }, + email_verified_at: { dataType: "datetime" }, }, additionalProperties: false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - AppDetails: { + FileMetadata: { dataType: "refObject", properties: { + version: { ref: "Version", required: true }, + user: { ref: "User", required: true }, + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + editable: { dataType: "boolean" }, + lintable: { dataType: "boolean" }, + extension: { dataType: "string", required: true }, + baseName: { dataType: "string", required: true }, + mime: { dataType: "string" }, + size_of_content: { dataType: "double", required: true }, + crc32: { dataType: "string", required: true }, + size_formatted: { dataType: "string", required: true }, name: { dataType: "string", required: true }, - slug: { dataType: "string", required: true }, - description: { dataType: "string", required: true }, - category_slug: { dataType: "string", required: true }, - user_name: { dataType: "string", required: true }, - devices: { + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "Record_Badge-at-slug.string_": { + dataType: "refAlias", + type: { + dataType: "nestedObjectLiteral", + nestedProperties: {}, + additionalProperties: { dataType: "string" }, + validators: {}, + }, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "Record_Badge-at-slug.Array__source-string--destination-string___": { + dataType: "refAlias", + type: { + dataType: "nestedObjectLiteral", + nestedProperties: {}, + additionalProperties: { dataType: "array", - array: { dataType: "string" }, + array: { + dataType: "nestedObjectLiteral", + nestedProperties: { + destination: { dataType: "string", required: true }, + source: { dataType: "string", required: true }, + }, + }, + }, + validators: {}, + }, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + AppMetadataJSON: { + dataType: "refObject", + properties: { + name: { dataType: "string" }, + description: { dataType: "string" }, + category: { ref: "AppCategoryName" }, + author: { dataType: "string" }, + icon: { dataType: "string" }, + license_file: { dataType: "string" }, + is_library: { dataType: "boolean" }, + is_hidden: { dataType: "boolean" }, + semantic_version: { dataType: "string" }, + interpreter: { dataType: "string" }, + main_executable: { dataType: "string" }, + main_executable_overrides: { ref: "Record_Badge-at-slug.string_" }, + file_mappings: { + dataType: "array", + array: { + dataType: "nestedObjectLiteral", + nestedProperties: { + destination: { dataType: "string", required: true }, + source: { dataType: "string", required: true }, + }, + }, + }, + file_mappings_overrides: { + ref: "Record_Badge-at-slug.Array__source-string--destination-string___", + }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + ProjectSlug: { + dataType: "refAlias", + type: { dataType: "string", validators: {} }, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + Dependency: { + dataType: "refObject", + properties: { + project_slug: { ref: "ProjectSlug", required: true }, + semantic_version_range: { dataType: "string", required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + ProjectStatusOnBadge: { + dataType: "refObject", + properties: { + badge: { ref: "Badge", required: true }, + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + status: { ref: "ProjectStatusName", required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + VoteFromUser: { + dataType: "refObject", + properties: { + type: { + dataType: "union", + subSchemas: [ + { dataType: "enum", enums: ["up"] }, + { dataType: "enum", enums: ["down"] }, + { dataType: "enum", enums: ["pig"] }, + ], required: true, }, + comment: { dataType: "string" }, + user: { ref: "User", required: true }, + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + WarningFromUser: { + dataType: "refObject", + properties: { + description: { dataType: "string", required: true }, + user: { ref: "User", required: true }, + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + Project: { + dataType: "refObject", + properties: { + slug: { dataType: "string", required: true }, + user_id: { dataType: "string", required: true }, + git: { dataType: "string" }, + allow_team_fixes: { dataType: "boolean" }, + created_at: { dataType: "datetime", required: true }, + updated_at: { dataType: "datetime", required: true }, + deleted_at: { dataType: "datetime" }, + name: { dataType: "string" }, + min_firmware: { dataType: "double" }, + max_firmware: { dataType: "double" }, + git_commit_id: { dataType: "string" }, + published_at: { dataType: "datetime" }, + download_counter: { dataType: "double" }, + license: { dataType: "string" }, + size_of_zip: { dataType: "double" }, + size_of_content: { dataType: "double" }, + category: { ref: "AppCategoryName", required: true }, + description: { dataType: "string" }, + revision: { dataType: "double" }, + status: { ref: "ProjectStatusName" }, + user_name: { dataType: "string" }, + interpreter: { dataType: "string" }, + version: { ref: "Version" }, + badges: { dataType: "array", array: { dataType: "string" } }, + dependencies: { + dataType: "array", + array: { dataType: "refObject", ref: "Dependency" }, + }, + states: { + dataType: "array", + array: { dataType: "refObject", ref: "ProjectStatusOnBadge" }, + }, + versions: { + dataType: "array", + array: { dataType: "refObject", ref: "Version" }, + }, + votes: { + dataType: "array", + array: { dataType: "refObject", ref: "VoteFromUser" }, + }, + warnings: { + dataType: "array", + array: { dataType: "refObject", ref: "WarningFromUser" }, + }, + collaborators: { + dataType: "array", + array: { dataType: "refObject", ref: "User" }, + }, }, additionalProperties: false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + Uint8Array: { + dataType: "refObject", + properties: {}, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa }; const templateService = new ExpressTemplateService(models, { noImplicitAdditionalProperties: "throw-on-extras", @@ -79,6 +336,7 @@ export function RegisterRoutes(app: Router) { // NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look // Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa // ########################################################################################################### + app.get( "/api/v3/devices", ...fetchMiddlewares(PublicRestController), @@ -205,11 +463,9 @@ export function RegisterRoutes(app: Router) { app.get( "/api/v3/apps/:slug", ...fetchMiddlewares(PublicRestController), - ...fetchMiddlewares( - PublicRestController.prototype.getAppDetails - ), + ...fetchMiddlewares(PublicRestController.prototype.getApp), - async function PublicRestController_getAppDetails( + async function PublicRestController_getApp( request: ExRequest, response: ExResponse, next: any @@ -238,7 +494,199 @@ export function RegisterRoutes(app: Router) { const controller = new PublicRestController(); await templateService.apiHandler({ - methodName: "getAppDetails", + methodName: "getApp", + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + } + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get( + "/api/v3/apps/:slug/files/latest/:filePath", + ...fetchMiddlewares(PublicRestController), + ...fetchMiddlewares( + PublicRestController.prototype.getLatestPublishedFile + ), + + async function PublicRestController_getLatestPublishedFile( + request: ExRequest, + response: ExResponse, + next: any + ) { + const args: Record = { + slug: { in: "path", name: "slug", required: true, dataType: "string" }, + filePath: { + in: "path", + name: "filePath", + required: true, + dataType: "string", + }, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ + args, + request, + response, + }); + + const controller = new PublicRestController(); + + await templateService.apiHandler({ + methodName: "getLatestPublishedFile", + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + } + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get( + "/api/v3/apps/:slug/files/rev:revision/:filePath", + ...fetchMiddlewares(PublicRestController), + ...fetchMiddlewares( + PublicRestController.prototype.getFileForVersion + ), + + async function PublicRestController_getFileForVersion( + request: ExRequest, + response: ExResponse, + next: any + ) { + const args: Record = { + slug: { in: "path", name: "slug", required: true, dataType: "string" }, + revision: { + in: "path", + name: "revision", + required: true, + dataType: "double", + }, + filePath: { + in: "path", + name: "filePath", + required: true, + dataType: "string", + }, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ + args, + request, + response, + }); + + const controller = new PublicRestController(); + + await templateService.apiHandler({ + methodName: "getFileForVersion", + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + } + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get( + "/api/v3/apps/:slug/zip/latest", + ...fetchMiddlewares(PublicRestController), + ...fetchMiddlewares( + PublicRestController.prototype.getLatestPublishedZip + ), + + async function PublicRestController_getLatestPublishedZip( + request: ExRequest, + response: ExResponse, + next: any + ) { + const args: Record = { + slug: { in: "path", name: "slug", required: true, dataType: "string" }, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ + args, + request, + response, + }); + + const controller = new PublicRestController(); + + await templateService.apiHandler({ + methodName: "getLatestPublishedZip", + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + } + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get( + "/api/v3/apps/:slug/zip/rev:revision", + ...fetchMiddlewares(PublicRestController), + ...fetchMiddlewares( + PublicRestController.prototype.getZipForVersion + ), + + async function PublicRestController_getZipForVersion( + request: ExRequest, + response: ExResponse, + next: any + ) { + const args: Record = { + slug: { in: "path", name: "slug", required: true, dataType: "string" }, + revision: { + in: "path", + name: "revision", + required: true, + dataType: "double", + }, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ + args, + request, + response, + }); + + const controller = new PublicRestController(); + + await templateService.apiHandler({ + methodName: "getZipForVersion", controller, response, next,